affaan-m/ECC · error · Error
Canonical session snapshot requires workers[${index}].output
Error message
Canonical session snapshot requires workers[${index}].outputs to be an object What it means
worker.outputs is missing or not a plain object. The schema requires outputs to contain three string arrays: summary, validation, remainingRisks. This guard fires before ensureArrayOfStrings on those fields, so any non-object outputs (null, undefined, array) is rejected.
Source
Thrown at scripts/lib/session-adapters/canonical-session.js:223
if (!isObject(worker.runtime)) {
throw new Error(`Canonical session snapshot requires workers[${index}].runtime to be an object`);
}
ensureString(worker.runtime.kind, `workers[${index}].runtime.kind`);
ensureOptionalString(worker.runtime.command, `workers[${index}].runtime.command`);
ensureBoolean(worker.runtime.active, `workers[${index}].runtime.active`);
ensureBoolean(worker.runtime.dead, `workers[${index}].runtime.dead`);
if (!isObject(worker.intent)) {
throw new Error(`Canonical session snapshot requires workers[${index}].intent to be an object`);
}
ensureStringAllowEmpty(worker.intent.objective, `workers[${index}].intent.objective`);
ensureArrayOfStrings(worker.intent.seedPaths, `workers[${index}].intent.seedPaths`);
if (!isObject(worker.outputs)) {
throw new Error(`Canonical session snapshot requires workers[${index}].outputs to be an object`);
}
ensureArrayOfStrings(worker.outputs.summary, `workers[${index}].outputs.summary`);
ensureArrayOfStrings(worker.outputs.validation, `workers[${index}].outputs.validation`);
ensureArrayOfStrings(worker.outputs.remainingRisks, `workers[${index}].outputs.remainingRisks`);
if (!isObject(worker.artifacts)) {
throw new Error(`Canonical session snapshot requires workers[${index}].artifacts to be an object`);
}
});
if (!isObject(snapshot.aggregates)) {
throw new Error('Canonical session snapshot requires aggregates to be an object');
}
ensureInteger(snapshot.aggregates.workerCount, 'aggregates.workerCount');
if (snapshot.aggregates.workerCount !== snapshot.workers.length) {
throw new Error('Canonical session snapshot requires aggregates.workerCount to match workers.length');View on GitHub (pinned to 01e15490f0)
Solutions
- Always emit outputs: { summary: [], validation: [], remainingRisks: [] } at minimum.
- For dmux, map from worker.handoff.summary/validation/remainingRisks with Array.isArray fallbacks; for claude-history, summary from metadata.completed, remainingRisks from metadata.notes.
- If migrating from an older shape, coerce each field to a string array before validation.
- Add a schema-shape unit test for every adapter's normalize* output.
Example fix
// before
const worker = { id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, intent:{...}, artifacts:{...} };
// after
const worker = {
id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, intent:{...},
outputs: {
summary: Array.isArray(metadata.completed) ? metadata.completed : [],
validation: [],
remainingRisks: metadata.notes ? [metadata.notes] : []
},
artifacts:{...}
}; Defensive patterns
Strategy: type-guard
Validate before calling
function ensureWorkerOutputs(worker) {
if (!worker.outputs || typeof worker.outputs !== 'object') {
worker.outputs = { summary: [], validation: [], remainingRisks: [] };
}
return worker;
} Type guard
function hasValidOutputs(w) {
const o = w.outputs;
if (o === null || typeof o !== 'object' || Array.isArray(o)) return false;
return ['summary','validation','remainingRisks'].every(k => Array.isArray(o[k]) && o[k].every(x => typeof x === 'string'));
} Try / catch
try { validateCanonicalSnapshot(snapshot); }
catch (err) {
if (err.message.includes('.outputs to be an object')) {
snapshot.workers.forEach(w => { if (!hasValidOutputs(w)) ensureWorkerOutputs(w); });
validateCanonicalSnapshot(snapshot);
} else throw err;
} Prevention
- Default outputs to {summary:[],validation:[],remainingRisks:[]} in every worker builder.
- Coerce source arrays with Array.isArray fallbacks as the dmux adapter does.
- Validate snapshot shape in adapter tests.
When it happens
Trigger: Custom adapter forgets the outputs block. Persisted recording was hand-edited to remove outputs. Adapter emits outputs as a flat string instead of an object of arrays.
Common situations: Building a new adapter for a session source that has no structured handoff yet and the author omits outputs entirely. Reading a recording written by an experimental branch that used a different outputs shape.
Related errors
- Canonical session snapshot requires workers[${index}] to be
- Canonical session snapshot requires workers[${index}].runtim
- Canonical session snapshot requires workers[${index}].intent
- Canonical session snapshot requires workers[${index}].artifa
- Canonical session snapshot requires aggregates to be an obje
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/0826dcbc545992ef.
Report an issue: GitHub.