Background
WorkflowRunHandle.result() in client/client.ts:325 polls the backend for workflow completion. The loop evaluates the timeout guard before checking the workflow status on each iteration.
Problem
The evaluation order inside the loop is:
- Fetch latest run status
- Check if (Date.now() - start > timeout) → throw
- Check if status is terminal → return output
When the workflow reaches a terminal status on the same poll cycle that the timeout fires, step 2 throws before step 3 executes. The result is discarded.
Steps to Reproduce
- Start a workflow that takes ~299ms to complete.
- Call handle.result({ timeoutMs: 300 }).
- If the 300ms poll and the workflow completion coincide, observe: Error: Timed out waiting for workflow run … to finish even though the workflow is in completed status.
Expected Behavior
When the fetched run status is terminal, the result is always returned (or the appropriate terminal error thrown), regardless of the timeout state.
Actual Behavior
A TimedOut error is raised and the completed output is lost. The caller must re-fetch the run manually to recover it.
Proposed Fix
Reorder the loop body in client/client.ts:330–365: check terminal status first, then timeout, then sleep. The timeout guard becomes a "bail-out before the next poll" check rather than a pre-terminal check.
Acceptance Criteria
Unit test: mock Date.now to return start + timeoutMs when a completed run is returned; assert result is returned, not error.
Unit test: mock Date.now to return start + timeoutMs + 1 with a running run; assert timeout error is thrown.
No change to the WorkflowRunHandle public API.
Background
WorkflowRunHandle.result() in client/client.ts:325 polls the backend for workflow completion. The loop evaluates the timeout guard before checking the workflow status on each iteration.
Problem
The evaluation order inside the loop is:
When the workflow reaches a terminal status on the same poll cycle that the timeout fires, step 2 throws before step 3 executes. The result is discarded.
Steps to Reproduce
Expected Behavior
When the fetched run status is terminal, the result is always returned (or the appropriate terminal error thrown), regardless of the timeout state.
Actual Behavior
A TimedOut error is raised and the completed output is lost. The caller must re-fetch the run manually to recover it.
Proposed Fix
Reorder the loop body in client/client.ts:330–365: check terminal status first, then timeout, then sleep. The timeout guard becomes a "bail-out before the next poll" check rather than a pre-terminal check.
Acceptance Criteria
Unit test: mock Date.now to return start + timeoutMs when a completed run is returned; assert result is returned, not error.
Unit test: mock Date.now to return start + timeoutMs + 1 with a running run; assert timeout error is thrown.
No change to the WorkflowRunHandle public API.