Skip to content

Errors

Flowneer wraps step failures in structured error types so you can distinguish flow errors from unexpected runtime errors and handle them appropriately.

FlowError

Thrown when a step (or sub-flow) fails after all retries are exhausted.

typescript
import { FlowError } from "flowneer";

try {
  await flow.run(shared);
} catch (err) {
  if (err instanceof FlowError) {
    console.error(`Flow failed at: ${err.step}`); // e.g. "step 2" or "batch (step 1)"
    console.error(`Caused by:`, err.cause); // the original error
  }
}

Properties

PropertyTypeDescription
stepstringHuman-readable step label, e.g. "step 2", "branch (step 0)"
causeunknownThe original error that caused the failure
messagestring"Flow failed at {step}: {cause.message}"

Step Labels

Step typeLabel format
fn"step N"
branch"branch (step N)"
loop"loop (step N)"
batch"batch (step N)"
parallel"parallel (step N)"

InterruptError

Thrown intentionally to pause a flow mid-execution — not a failure. Used by interruptIf and humanNode to implement human-in-the-loop and approval gates.

typescript
import { InterruptError } from "flowneer";

try {
  await flow.run(shared);
} catch (err) {
  if (err instanceof InterruptError) {
    const savedState = err.savedShared; // deep clone of shared at interrupt time
    // save to DB, prompt user, etc.
    const userInput = await promptUser(savedState.__humanPrompt);
    // resume the flow
    await resumeFlow(flow, savedState, { feedback: userInput }, resumeFromStep);
  }
}

Properties

PropertyTypeDescription
savedSharedunknownDeep clone (JSON.parse/stringify) of shared state at interrupt time
messagestring"Flow interrupted"

InterruptError is never wrapped in a FlowError — it propagates directly so callers can catch it cleanly.


onError Hook

Plugins and callbacks can listen for step errors without stopping the flow via the onError hook:

typescript
this._setHooks({
  onError: (meta, error, shared) => {
    console.error(`Step ${meta.index} failed:`, error);
    shared.lastError = error instanceof Error ? error.message : String(error);
  },
});

onError fires before the error is propagated — it's informational, not a recovery mechanism. To recover, use withFallback.


Error Handling Patterns

Retry on failure

typescript
.then(riskyStep, { retries: 3, delaySec: 2 })

Fallback on failure

typescript
const AppFlow = FlowBuilder.extend([withFallback]);
const flow = new AppFlow();
flow.withFallback(async (s) => {
  s.result = s.__fallbackError.message;
  // flow continues normally after this
});

Circuit breaker

typescript
const AppFlow = FlowBuilder.extend([withCircuitBreaker]);
const flow = new AppFlow();
flow.withCircuitBreaker({ maxFailures: 3, resetMs: 30_000 });

Timeout

typescript
const AppFlow = FlowBuilder.extend([withTimeout]);
const flow = new AppFlow();
flow.withTimeout(5000); // 5 s per step
// or per-step:
.then(slowStep, { timeoutMs: 10_000 })