affaan-m/ECC · error · Error
Canonical session snapshot requires aggregates.states to be
Error message
Canonical session snapshot requires aggregates.states to be an object
What it means
aggregates.states is missing or not a plain object. The schema requires states to be a map of worker-state string keys to non-negative integer counts (e.g. { active: 2, completed: 1 }). buildAggregates produces this from workers; the guard fires on hand-built or stale snapshots where states is null/undefined/array.
Source
Thrown at scripts/lib/session-adapters/canonical-session.js:245
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');
}
if (!isObject(snapshot.aggregates.states)) {
throw new Error('Canonical session snapshot requires aggregates.states to be an object');
}
if (!isObject(snapshot.aggregates.healths)) {
throw new Error('Canonical session snapshot requires aggregates.healths to be an object');
}
for (const [state, count] of Object.entries(snapshot.aggregates.states)) {
ensureString(state, 'aggregates.states key');
ensureInteger(count, `aggregates.states.${state}`);
}
for (const [health, count] of Object.entries(snapshot.aggregates.healths)) {
ensureString(health, 'aggregates.healths key');
ensureInteger(count, `aggregates.healths.${health}`);
}
return snapshot;
}View on GitHub (pinned to 01e15490f0)
Solutions
- Use buildAggregates(workers); it always returns states as an object (possibly empty {}).
- If building by hand for zero workers, use states: {} not null.
- When loading legacy recordings, coerce arrays of {state,count} into a map before validation.
- Re-run validateCanonicalSnapshot after any aggregate change.
Example fix
// before
aggregates: { workerCount: 0, states: null, healths: {} }
// after
aggregates: buildAggregates(workers) // yields e.g. { workerCount: 0, states: {}, healths: {} } Defensive patterns
Strategy: type-guard
Validate before calling
function ensureStatesMap(snapshot) {
const a = snapshot.aggregates || (snapshot.aggregates = {});
if (a.states === null || typeof a.states !== 'object' || Array.isArray(a.states)) {
a.states = (snapshot.workers || []).reduce((m, w) => { const s = w.state || 'unknown'; m[s] = (m[s]||0)+1; return m; }, {});
}
return snapshot;
} Type guard
function hasValidStatesMap(s) {
const st = s.aggregates && s.aggregates.states;
return st !== null && typeof st === 'object' && !Array.isArray(st)
&& Object.entries(st).every(([k,v]) => typeof k === 'string' && Number.isInteger(v) && v >= 0);
} Try / catch
try { validateCanonicalSnapshot(snapshot); }
catch (err) {
if (err.message.includes('aggregates.states to be an object')) {
snapshot.aggregates = buildAggregates(snapshot.workers || []);
validateCanonicalSnapshot(snapshot);
} else throw err;
} Prevention
- Use buildAggregates so states is always a map.
- For zero workers, use states:{} not null.
- Migrate legacy array-based state summaries into maps before validation.
When it happens
Trigger: Hand-built snapshot omits states. Recording written by an adapter version that used an array of state objects instead of a map. aggregates.states set to null because no workers were active.
Common situations: Custom adapter constructs aggregates manually and forgets states. Empty-workers case where the author set states:null instead of states:{}.
Related errors
- Canonical session snapshot requires aggregates to be an obje
- Canonical session snapshot requires aggregates.workerCount t
- Canonical session snapshot requires aggregates.healths to be
- Canonical session snapshot requires workers[${index}] to be
- Canonical session snapshot requires workers[${index}].runtim
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/93df044b91e0c325.
Report an issue: GitHub.