Viewing Workflow 5 (Pre-release) Documentation.
Go to Workflow 4 (Latest)

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.

On this page

GitHubEdit this page on GitHub