affaan-m/ECC · error · Error
Canonical session snapshot requires aggregates.healths to be
Error message
Canonical session snapshot requires aggregates.healths to be an object
What it means
aggregates.healths is missing or not a plain object. Symmetric to states: must be a map of health string keys (healthy/degraded/stale/unknown) to non-negative integer counts. buildAggregates derives this from worker.health on each worker.
Source
Thrown at scripts/lib/session-adapters/canonical-session.js:249
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;
}
function resolveRecordingDir(options = {}) {
if (typeof options.recordingDir === 'string' && options.recordingDir.length > 0) {
return path.resolve(options.recordingDir);View on GitHub (pinned to 01e15490f0)
Solutions
- Use buildAggregates(workers); it always returns healths as an object (possibly empty {}).
- If building by hand for zero workers, use healths: {} not null.
- When adapting a source with no health signal, derive health via deriveWorkerHealth(rawWorker) before building aggregates.
- Pin a unit test that asserts both states and healths are objects after buildAggregates.
Example fix
// before
aggregates: { workerCount: workers.length, states: buildStates(workers), healths: null }
// after
aggregates: buildAggregates(workers) // yields { workerCount, states, healths } together Defensive patterns
Strategy: type-guard
Validate before calling
function ensureHealthsMap(snapshot) {
const a = snapshot.aggregates || (snapshot.aggregates = {});
if (a.healths === null || typeof a.healths !== 'object' || Array.isArray(a.healths)) {
a.healths = (snapshot.workers || []).reduce((m, w) => { const h = w.health || 'unknown'; m[h] = (m[h]||0)+1; return m; }, {});
}
return snapshot;
} Type guard
function hasValidHealthsMap(s) {
const h = s.aggregates && s.aggregates.healths;
return h !== null && typeof h === 'object' && !Array.isArray(h)
&& Object.entries(h).every(([k,v]) => typeof k === 'string' && Number.isInteger(v) && v >= 0);
} Try / catch
try { validateCanonicalSnapshot(snapshot); }
catch (err) {
if (err.message.includes('aggregates.healths to be an object')) {
snapshot.aggregates = buildAggregates(snapshot.workers || []);
validateCanonicalSnapshot(snapshot);
} else throw err;
} Prevention
- Always derive health via deriveWorkerHealth before building aggregates.
- Use buildAggregates so healths is always a map.
- For zero workers, use healths:{} not null.
When it happens
Trigger: Hand-built snapshot omits healths. Adapter version that stored only states and skipped healths. Empty-workers case where author set healths:null.
Common situations: Custom adapter that does not classify worker health yet and omits the field. Migration from a schema that only tracked states.
Related errors
- Canonical session snapshot requires aggregates to be an obje
- Canonical session snapshot requires aggregates.workerCount t
- Canonical session snapshot requires aggregates.states 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/03cab321455250a4.
Report an issue: GitHub.