Skip to content

Error handling

footprintjs preserves structured error information through the recorder pipeline. When a stage throws, the error flows to both the narrative and any attached FlowRecorder — with field-level details intact.

When a .contract() input schema fails validation, footprintjs throws InputValidationError with field-level issues:

import { flowChart, FlowChartExecutor, InputValidationError } from 'footprintjs';
import { z } from 'zod';
const chart = flowChart<{ name: string; age: number }>('Process', async (scope) => {
console.log(scope.name, scope.age);
}, 'process')
.contract({
input: z.object({
name: z.string().min(1),
age: z.number().positive(),
}),
})
.build();
const executor = new FlowChartExecutor(chart);
try {
await executor.run({ input: { name: '', age: -5 } });
} catch (err) {
if (err instanceof InputValidationError) {
console.log(err.issues);
// [{ path: ['name'], message: 'String must contain at least 1 character(s)', code: 'too_small' },
// { path: ['age'], message: 'Number must be greater than 0', code: 'too_small' }]
}
}

You can also throw InputValidationError from any stage for custom validation:

.addFunction('Validate', async (scope) => {
const issues = [];
if (scope.total < 0) {
issues.push({ path: ['total'], message: 'Must be positive', code: 'too_small' });
}
if (issues.length > 0) {
throw new InputValidationError('Order validation failed', issues);
}
}, 'validate')

FlowRecorder.onError receives a FlowErrorEvent with structured details — including field-level issues for validation errors:

import type { FlowRecorder, FlowErrorEvent } from 'footprintjs';
const errorObserver: FlowRecorder = {
id: 'error-observer',
onError(event: FlowErrorEvent) {
console.log(`Error at: ${event.stageName}`);
console.log(`Type: ${event.structuredError.name}`);
console.log(`Code: ${event.structuredError.code}`);
if (event.structuredError.issues) {
for (const issue of event.structuredError.issues) {
console.log(` ${issue.path.join('.')}: ${issue.message}`);
}
}
},
};
executor.attachFlowRecorder(errorObserver);

extractErrorInfo and formatErrorInfo normalize any thrown value into a consistent structure:

import { extractErrorInfo, formatErrorInfo } from 'footprintjs';
const info = extractErrorInfo(error);
// { name: 'InputValidationError', code: 'VALIDATION', message: '...', issues: [...] }
console.log(formatErrorInfo(info));
// InputValidationError [VALIDATION]: Order validation failed
// total: Must be positive [too_small]
// email: Required [invalid_type]

Works with InputValidationError, standard Error, Node.js errors with .code, and even non-Error thrown values.