affaan-m/ECC · error · Error
Canonical session snapshot requires aggregates to be an obje
Error message
Canonical session snapshot requires aggregates to be an object
What it means
snapshot.aggregates is missing or not a plain object. Aggregates must hold workerCount (non-negative integer), states (object of state->count), and healths (object of health->count). The built-in normalizers always build aggregates via buildAggregates(workers); this fires mainly when a snapshot is hand-built or loaded from a tampered/stale recording without aggregates.
Source
Thrown at scripts/lib/session-adapters/canonical-session.js:236
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');
}
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}`);View on GitHub (pinned to 01e15490f0)
Solutions
- Use buildAggregates(workers) exported from canonical-session to compute the object in one call: aggregates: buildAggregates(workers).
- If building by hand, set { workerCount: workers.length, states: {...}, healths: {...} }.
- When loading a recording missing aggregates, recompute them from workers before re-validating.
- Always pass the full snapshot through one of the normalize*Session builders; they call buildAggregates for you.
Example fix
// before
const { buildAggregates } = require('./canonical-session');
const snapshot = { schemaVersion:'ecc.session.v1', adapterId:'custom', session:{...}, workers }; // no aggregates
// after
const snapshot = {
schemaVersion:'ecc.session.v1', adapterId:'custom', session:{...}, workers,
aggregates: buildAggregates(workers)
}; Defensive patterns
Strategy: validation
Validate before calling
function ensureAggregates(snapshot) {
if (!snapshot.aggregates || typeof snapshot.aggregates !== 'object') {
snapshot.aggregates = buildAggregates(snapshot.workers || []);
}
return snapshot;
} Type guard
function hasValidAggregates(s) {
const a = s.aggregates;
return a !== null && typeof a === 'object' && !Array.isArray(a)
&& Number.isInteger(a.workerCount) && a.workerCount === (s.workers || []).length
&& a.states !== null && typeof a.states === 'object'
&& a.healths !== null && typeof a.healths === 'object';
} Try / catch
try { validateCanonicalSnapshot(snapshot); }
catch (err) {
if (err.message.includes('aggregates to be an object')) {
snapshot.aggregates = buildAggregates(snapshot.workers || []);
validateCanonicalSnapshot(snapshot);
} else throw err;
} Prevention
- Always use buildAggregates(workers) to produce aggregates.
- Never omit aggregates when hand-building snapshots.
- Recompute aggregates whenever workers change.
When it happens
Trigger: Calling validateCanonicalSnapshot with a snapshot assembled manually that omits aggregates. Loading a recording written by an older schema that did not persist aggregates. Setting aggregates:null because workerCount is unknown.
Common situations: User constructs a snapshot from raw session data and forgets to compute aggregates. Test fixture that builds only workers. Adapter migration that drops aggregates.
Related errors
- Canonical session snapshot requires aggregates.workerCount t
- Canonical session snapshot requires aggregates.states to be
- 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/1b4bfb1928b36ff0.
Report an issue: GitHub.