abort-signal-timeout-in-workflow
AbortSignal.timeout() cannot be used inside workflow functions because it relies on real timers which break deterministic replay.
Error
AbortSignal.timeout() is not supported in workflow functions.
Use sleep() with an AbortController instead.Why This Happens
AbortSignal.timeout() creates a signal that aborts after a real-time delay using an internal timer. Workflow functions must be deterministic to support replay — they run the same code multiple times during the workflow's lifecycle, using the event log to resume execution to the correct point.
Real-time timers break this determinism because:
- On the first execution, the timer might fire after 10 seconds
- On replay, the timer would fire again, but the event log may have already advanced past that point
- The timer's behavior depends on wall-clock time, which varies between executions
How to Fix
Use sleep() with an AbortController to create a deterministic timeout that cancels in-flight work:
Before (incorrect):
export async function workflow() {
"use workflow";
// This will throw an error
const signal = AbortSignal.timeout(10_000);
const result = await fetchData(signal);
return result;
}After (correct):
import { sleep } from "workflow";
export async function workflow() {
"use workflow";
const controller = new AbortController();
void sleep("10s").then(() => controller.abort());
return await fetchData(controller.signal);
}
async function fetchData(signal: AbortSignal) {
"use step";
const response = await fetch("https://api.example.com/data", { signal });
return response.json();
}The sleep() + AbortController pattern is the durable equivalent of AbortSignal.timeout(). The sleep is recorded in the event log, so it replays deterministically. If fetchData finishes within 10 seconds you get the response; if not, the timer fires controller.abort(), fetch rejects with an AbortError, and the step's failure propagates to the workflow as a FatalError (no retries — abort is intentional cancellation).
AbortSignal.timeout() works normally inside step functions, since steps have full Node.js runtime access and are not replayed.
Related
- Cancellation — Patterns for cancelling in-flight work
sleep()API Reference — Durable sleep primitive- Workflows and Steps — Why workflow functions must be deterministic
setTimeoutin Workflow — Similar restriction onsetTimeout