Stagehand

Errors

Every error class Stagehand throws or resolves with, what triggers each one, and the isAbort helper.

All error classes extend Error and set name to their own class name, so error.name and instanceof agree. Most carry extra fields relevant to what failed, listed below each one.

Build-time errors

Thrown synchronously while a script is being assembled, from addStep, addPhase, or use(), before run() is ever called.

StepDefinitionError

class StepDefinitionError extends Error {
  readonly step: string;
}

Thrown by addStep when clean names a key reserved by some step’s rollbackKeys, including a key that step reserves for itself. Also thrown by use() if the value passed is neither a Routine (from routineFor) nor a Script, and by the runtime cache handle if a step addresses a slot name that was never declared as a cached phase or step. See Rollbacks and Cleaning Context Keys.

DuplicateNameError

class DuplicateNameError extends Error {
  readonly kind: "phase" | "step";
  readonly duplicate: string;
}

Thrown by addPhase when a phase name is already used anywhere in the script (including one brought in by use()), and by addStep when a step name is already used within its own phase. The same step name in two different phases is fine. Mounting the same routine twice without giving each mount an as prefix is the most common trigger; see Reusable Scripts and Mounts.

Run-time errors

SchemaValidationError

class SchemaValidationError extends Error {
  readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
}

Thrown by run() when input fails the schema given to defineInput, before any phase executes. message joins every issue’s path and message; issues is the raw Standard Schema issue list for programmatic handling. See The Typed Context. Always thrown, never returned as part of RunResult — there is no partial result to produce when the input itself was rejected.

StepTimeoutError

class StepTimeoutError extends Error {
  readonly step: string;
  readonly timeoutMs: number;
}

The reason a step’s signal aborts when its timeoutMs elapses before the handler resolves. Surfaces as result.error on a failed run, or is thrown directly when throwOnError is set. Counts as a failed attempt against retry, subject to retryIf like any other error.

ScriptAbortedError

class ScriptAbortedError extends Error {
  readonly reason: string;
}

The reason a run’s signal aborts on Ctrl-C / SIGTERM (when handleSignals is on) or on an external AbortSignal passed to run(input, { signal }). result.status is "aborted" rather than "failed" when this is what stopped the run.

RollbackFailedError

class RollbackFailedError extends Error {
  readonly phase: string;
  readonly step: string;
  readonly rollbackError: unknown;
}

Describes a compensation handler throwing during an unwind. In practice, a failing rollback does not propagate as this error type through RunResult: it is recorded in result.rollbacks as { ok: false, error } instead, and never replaces result.error, which always stays the failure that triggered the unwind in the first place. RollbackFailedError is exported for callers building their own reporting around a rollback failure. See Rollbacks.

StepFailedError

class StepFailedError extends Error {
  readonly phase: string;
  readonly step: string;
  readonly attempts: number;
}

Wraps a handler’s thrown error with the step and phase it happened in, and how many attempts were made, using the standard cause mechanism (error.cause is whatever the handler actually threw). Exported for use in a caller’s own error handling; result.error on a failed run is the original thrown value, not automatically wrapped in this type.

Cache errors

CacheShapeError

class CacheShapeError extends Error {
  readonly slot: string;
  readonly path: string; // e.g. "orders.0.date_fixed"
}

Thrown the moment code reads a property from a cached value (via context.cache.read) that the stored JSON does not actually have, unless the slot declared a schema or the read passed { raw: true }. path names the exact property access that failed, dotted through arrays and objects. See Caching.

isAbort(error)

function isAbort(error: unknown): boolean

Returns true for a ScriptAbortedError, or any Error whose name is "AbortError" (the DOM/Node convention other abort-aware APIs use). Use it to tell a genuine cancellation apart from an ordinary handler failure when inspecting result.error:

if (!result.ok) {
  if (isAbort(result.error)) {
    console.log("canceled from outside");
  } else {
    console.error(result.error);
  }
}