affaan-m/ECC · error · Error
Canonical session snapshot requires workers[${index}].runtim
Error message
Canonical session snapshot requires workers[${index}].runtime to be an object What it means
A workers[index] entry passed the object check but its `runtime` field failed isObject(). The schema requires worker.runtime to be an object containing kind (string), command (string|null), active (boolean), and dead (boolean). This guard fires before the per-field ensure* checks on runtime, so any non-object runtime (undefined, null, string) is rejected.
Source
Thrown at scripts/lib/session-adapters/canonical-session.js:207
if (!Array.isArray(snapshot.workers)) {
throw new Error('Canonical session snapshot requires workers to be an array');
}
snapshot.workers.forEach((worker, index) => {
if (!isObject(worker)) {
throw new Error(`Canonical session snapshot requires workers[${index}] to be an object`);
}
ensureString(worker.id, `workers[${index}].id`);
ensureString(worker.label, `workers[${index}].label`);
ensureString(worker.state, `workers[${index}].state`);
ensureString(worker.health, `workers[${index}].health`);
ensureOptionalString(worker.branch, `workers[${index}].branch`);
ensureOptionalString(worker.worktree, `workers[${index}].worktree`);
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`);
}
View on GitHub (pinned to 01e15490f0)
Solutions
- Ensure each worker has runtime: { kind: '<string>', command: <string|null>, active: <boolean>, dead: <boolean> }.
- If adapting a dmux pane, follow normalizeDmuxSnapshot: set kind:'tmux-pane', command from pane.currentCommand||null, active/dead from pane booleans.
- If the source genuinely has no runtime, still emit a minimal runtime object (e.g. kind:'unknown', command:null, active:false, dead:true) rather than omitting it.
- Validate with a tiny unit snapshot before persisting to surface the exact missing field.
Example fix
// before
const worker = { id: 'w1', label: 'w1', state: 'active', health: 'healthy', runtime: null, intent: {...}, outputs: {...}, artifacts: {...} };
// after
const worker = {
id: 'w1', label: 'w1', state: 'active', health: 'healthy',
runtime: { kind: 'tmux-pane', command: pane.currentCommand || null, active: Boolean(pane.active), dead: Boolean(pane.dead) },
intent: {...}, outputs: {...}, artifacts: {...}
}; Defensive patterns
Strategy: type-guard
Validate before calling
function ensureWorkerRuntime(worker) {
if (!worker.runtime || typeof worker.runtime !== 'object') {
worker.runtime = { kind: 'unknown', command: null, active: false, dead: true };
}
return worker;
} Type guard
function hasValidRuntime(w) {
const r = w.runtime;
return r !== null && typeof r === 'object' && !Array.isArray(r)
&& typeof r.kind === 'string'
&& typeof r.active === 'boolean' && typeof r.dead === 'boolean';
} Try / catch
try { validateCanonicalSnapshot(snapshot); }
catch (err) {
if (err.message.includes('.runtime to be an object')) {
snapshot.workers.forEach(w => { if (!hasValidRuntime(w)) ensureWorkerRuntime(w); });
validateCanonicalSnapshot(snapshot);
} else throw err;
} Prevention
- Centralize worker construction in one builder per adapter.
- Always emit a runtime object even for dead/recorded workers.
- Run validateCanonicalSnapshot in adapter unit tests.
When it happens
Trigger: A custom adapter builds a worker object but forgets the runtime block, or sets runtime:null. Hand-authored snapshot JSON omits the runtime key. A migrated snapshot from a pre-runtime schema version where runtime was a string command name.
Common situations: Extending the dmux-tmux adapter to a new pane source and forgetting to populate runtime. Editing a recording JSON by hand and deleting the runtime object. Caching a worker across schema versions without re-adding runtime.
Related errors
- Canonical session snapshot requires workers[${index}] to be
- Canonical session snapshot requires workers[${index}].intent
- Canonical session snapshot requires workers[${index}].output
- 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/c54b1bf5f75c4f4e.
Report an issue: GitHub.