Summary
A production Vercel Workflow using @workflow/core@4.6.1 repeatedly fails during recovery replay with CORRUPTED_EVENT_LOG when an ordered batch of multiple hooks is raced against sleep():
const hooks = children.map(() => createHook<Result>());
const outcome = await Promise.race([
Promise.all(hooks.map(async (hook) => await hook)),
sleep("35m"),
]);
The run made normal forward progress for approximately 41 minutes before a recovery replay diverged. The runtime attempted three recovery replays and then marked the run failed after the fourth divergence.
This looks related to #2171, but appears to reproduce on @workflow/core@4.6.1 with the multi-hook Promise.all(...) variant.
Actual behavior
The terminal workflow error was:
CORRUPTED_EVENT_LOG
Workflow replay diverged 4 times after 3 recovery replays.
Last divergence: Replay could not consume event:
eventType=hook_created,
correlationId=<hook-id>,
eventId=<event-id>.
The workflow's application/domain ledger remained in its last running state because the provider failed before finalization.
Expected behavior
Recovery replay should consume the committed hook and wait events in deterministic event-log order and continue from the last durable checkpoint.
Environment
- Vercel Workflows, production
- Region:
iad1
workflow: 4.6.1
@workflow/core: 4.6.1
@workflow/world-vercel: 4.5.1
@workflow/next: 4.1.1
- Next.js:
16.2.4
- Workflow spec version shown in the trace:
3
- Batch concurrency:
8
Additional context
The original application used a sliding worker pool. We first removed that source of nondeterminism and changed it to fixed, ledger-ordered batches: hooks are now created sequentially in child order, each queue step is awaited, and the batch does not advance until the current batch settles.
The remaining orchestration primitive is effectively:
for (const batch of orderedBatches(children, 8)) {
const hooks = [];
for (const child of batch) {
const hook = createHook<Result>();
hooks.push({ child, hook });
await queueChildStep(child, hook.token);
}
await Promise.race([
Promise.all(hooks.map(({ hook }) => hook)),
sleep("35m"),
]);
}
The failure happened after many batches had completed successfully, during a provider recovery replay. The divergent event was hook_created, not an application step failure, queue delivery error, or hook payload validation error.
PR #2171 describes a hook-versus-sleep replay race caused by delivery/microtask ordering. Since this run used @workflow/core@4.6.1, I suspect either:
- the multiple-hook
Promise.all shape exposes an uncovered variant, or
- there is a regression in the later runtime.
I can provide the private Vercel workflow run ID, deployment ID, exact event ID, correlation ID, and full trace directly to a maintainer through a non-public channel.
Summary
A production Vercel Workflow using
@workflow/core@4.6.1repeatedly fails during recovery replay withCORRUPTED_EVENT_LOGwhen an ordered batch of multiple hooks is raced againstsleep():The run made normal forward progress for approximately 41 minutes before a recovery replay diverged. The runtime attempted three recovery replays and then marked the run failed after the fourth divergence.
This looks related to #2171, but appears to reproduce on
@workflow/core@4.6.1with the multi-hookPromise.all(...)variant.Actual behavior
The terminal workflow error was:
The workflow's application/domain ledger remained in its last running state because the provider failed before finalization.
Expected behavior
Recovery replay should consume the committed hook and wait events in deterministic event-log order and continue from the last durable checkpoint.
Environment
iad1workflow:4.6.1@workflow/core:4.6.1@workflow/world-vercel:4.5.1@workflow/next:4.1.116.2.438Additional context
The original application used a sliding worker pool. We first removed that source of nondeterminism and changed it to fixed, ledger-ordered batches: hooks are now created sequentially in child order, each queue step is awaited, and the batch does not advance until the current batch settles.
The remaining orchestration primitive is effectively:
The failure happened after many batches had completed successfully, during a provider recovery replay. The divergent event was
hook_created, not an application step failure, queue delivery error, or hook payload validation error.PR #2171 describes a hook-versus-sleep replay race caused by delivery/microtask ordering. Since this run used
@workflow/core@4.6.1, I suspect either:Promise.allshape exposes an uncovered variant, orI can provide the private Vercel workflow run ID, deployment ID, exact event ID, correlation ID, and full trace directly to a maintainer through a non-public channel.