Files
vercel__workflow/.changeset/replay-timeout-excludes-step-bodies.md
Nathan Rajlich 2a446af517 [core] Exclude inline step execution from replay timeout (#2013)
* [core] Exclude inline step execution from replay timeout

The v5 combined workflow+step handler wraps inline step bodies in the
same setTimeout(..., REPLAY_TIMEOUT_MS) guard that previously only
bounded the v4 'workflows' function's fast deterministic replay. As a
result, any workflow with a single step exceeding 240s hard-fails with
FatalError: Workflow replay exceeded maximum duration (240s) after 4
attempts — even though the step could legitimately run for the full
function maxDuration (up to 800s on Pro Fluid).

Replace the setTimeout guard with a per-invocation budget that only
accumulates non-step time. pauseReplayBudget() / resumeReplayBudget()
bracket each executeStep() call, and the loop checks the budget at
iteration boundaries. The retry-then-fail semantics from #1567 are
preserved verbatim for the pure-replay case.

Also adds a WORKFLOW_REPLAY_TIMEOUT_MS env var override (clamped to
30s..780s) so operators can adjust the bare-replay ceiling without
patching @workflow/core.

Fixes #2009.

* Address PR review feedback

- Extract budget bookkeeping into ReplayBudget class (replay-budget.ts)
  with sentinel-protected idempotent pause()/resume() to avoid
  double-counting in future refactors that nest step execution
- Restore VERCEL_URL gate around process.exit(1) so a long pure-replay
  in local dev/non-Vercel runtimes can't hard-kill the host process
- Warn (once per distinct raw value) when WORKFLOW_REPLAY_TIMEOUT_MS is
  clamped or rejected, so misconfiguration is observable
- Correct Hobby maxDuration comment (60s standard / 300s Fluid)
- Document budget-check responsiveness trade-off vs. old setTimeout
- Tighten describe-error test assertions to match the full new hint
- Shorten changeset description
- Add ReplayBudget unit tests (9) including 8-minute step regression
- Add warn-once tests for getReplayTimeoutMs (extended)

* Replace VERCEL_URL gate with World capability

Per review feedback, gating runtime behavior on process.env.VERCEL_URL
leaks deployment-environment concerns into @workflow/core. Replace the
check with a new optional capability on the World interface:
processExitTriggersQueueRedelivery (default false).

- @workflow/world: declare the new optional capability on World
- @workflow/world-vercel: set it to true (Vercel fails the function
  invocation on non-zero exit and VQS redelivers via fresh invocation)
- @workflow/core: handleReplayBudgetExhausted reads world.processExitTriggersQueueRedelivery
  instead of process.env.VERCEL_URL; behavior is otherwise unchanged
- Add 4 unit tests for handleReplayBudgetExhausted covering both branches
  (exit-for-redelivery and best-effort run_failed)

---------

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-05-22 00:32:36 +00:00

404 B

@workflow/core, @workflow/world, @workflow/world-vercel
@workflow/core @workflow/world @workflow/world-vercel
patch patch patch

Exclude inline step execution from the workflow replay timeout. Long-running steps no longer hit REPLAY_TIMEOUT (fixes #2009). Adds a WORKFLOW_REPLAY_TIMEOUT_MS env var override and a new optional World.processExitTriggersQueueRedelivery capability used to gate the runtime's process.exit(1) failure path.