SPB Git

Repository Public

errors.ts 4.2 KB 120 lines typescript

1/** Error classes of the kernel. Public ones are part of the contract; control-flow ones are internal. */23export class KernelError extends Error {4  constructor(message: string, options?: { cause?: unknown }) {5    super(message, options);6    this.name = new.target.name;7  }8}910/** Run logic made a different call at a given seq than the one recorded in the journal. */11export class NonDeterminismError extends KernelError {12  constructor(13    readonly runId: string,14    readonly seq: number,15    detail: string,16  ) {17    super(`Non-deterministic run logic at step ${seq} of run ${runId}: ${detail}`);18  }19}2021/** The lease was lost (fencing token changed, run no longer 'running', or heartbeat failed). */22export class LeaseLostError extends KernelError {23  constructor(readonly runId: string, detail = 'fencing check failed') {24    super(`Lease lost for run ${runId}: ${detail}`);25  }26}2728export type BudgetReason = 'run_budget' | 'mission_budget' | 'daily_cap';29/** Thrown by ctx.step for model_call steps once the budget is exhausted. Journaled, so replay rethrows it. */30export class BudgetExhaustedError extends KernelError {31  constructor(readonly reason: BudgetReason, message?: string) {32    super(message ?? `Budget exhausted (${reason})`);33  }34}3536/** A step whose exec threw. Identical on first execution and on replay (rebuilt from the journal). */37export class StepError extends KernelError {38  readonly stepSeq: number;39  readonly stepName: string;40  readonly errorName: string;41  readonly code: string | number | null;42  readonly status: number | null;43  readonly retryable: boolean | null;44  constructor(seq: number, stepName: string, e: RecordedError) {45    super(e.message);46    this.stepSeq = seq;47    this.stepName = stepName;48    this.errorName = e.name;49    this.code = e.code ?? null;50    this.status = e.status ?? null;51    this.retryable = e.retryable ?? null;52  }53}5455/** The kernel could not read/write its own state (database unavailable…). The run is left resumable. */56export class KernelStorageError extends KernelError {}5758export class WaitNotFoundError extends KernelError {}5960/** What is stored in steps.error. Never contains stacks or request bodies. */61export interface RecordedError {62  name: string;63  message: string;64  code?: string | number;65  status?: number;66  retryable?: boolean;67  reason?: string;68}6970export function recordError(err: unknown): RecordedError {71  if (!(err instanceof Error)) return { name: 'Error', message: String(err).slice(0, 4000) };72  const e = err as Error & { code?: unknown; status?: unknown; retryable?: unknown };73  const out: RecordedError = { name: e.name || 'Error', message: String(e.message ?? '').slice(0, 4000) };74  if (typeof e.code === 'string' || typeof e.code === 'number') out.code = e.code;75  if (typeof e.status === 'number') out.status = e.status;76  if (typeof e.retryable === 'boolean') out.retryable = e.retryable;77  return out;78}7980// ------------------------------------------------------------------------------------------81// Internal control flow (exported for the kernel's own modules and tests; never caught by logic).8283/** The run was suspended (durable wait / timer): the lease is released, the logic must unwind. */84export class Suspend extends KernelError {85  constructor() {86    super('run suspended (durable wait)');87  }88}89export class RunCancelled extends KernelError {90  constructor() {91    super('run cancelled');92  }93}94export class WorkerStopping extends KernelError {95  constructor() {96    super('worker stopping');97  }98}99export class ReconcileAborted extends KernelError {100  constructor(readonly seq: number, readonly stepName: string) {101    super(`Aborted by the owner while reconciling step ${seq} (${stepName})`);102  }103}104105/**106 * True for the errors that end the current execution of a run (suspension, cancel, shutdown, lost107 * lease, storage failure, reconcile abort). Run logic with a broad `catch` should rethrow these; if108 * it does not, the context stays closed and the kernel still settles the run correctly.109 */110export function isControlError(e: unknown): boolean {111  return (112    e instanceof Suspend ||113    e instanceof RunCancelled ||114    e instanceof WorkerStopping ||115    e instanceof LeaseLostError ||116    e instanceof KernelStorageError ||117    e instanceof ReconcileAborted118  );119}120
Files main
Menu

Theme