{"record":{"id":"c57fa26dae80a1de","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-must-be-an-object","errorCode":null,"errorMessage":"Canonical session snapshot must be an object","messagePattern":"Canonical session snapshot must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":164,"sourceCode":"  const failedCount = (workerStates.failed || 0) + (workerStates.error || 0);\n  if (failedCount > 0) {\n    return 'failed';\n  }\n\n  const completedCount = (workerStates.completed || 0)\n    + (workerStates.succeeded || 0)\n    + (workerStates.success || 0)\n    + (workerStates.done || 0);\n  if (completedCount === totalWorkers) {\n    return 'completed';\n  }\n\n  return 'idle';\n}\n\nfunction validateCanonicalSnapshot(snapshot) {\n  if (!isObject(snapshot)) {\n    throw new Error('Canonical session snapshot must be an object');\n  }\n\n  ensureString(snapshot.schemaVersion, 'schemaVersion');\n  if (snapshot.schemaVersion !== SESSION_SCHEMA_VERSION) {\n    throw new Error(`Unsupported canonical session schema version: ${snapshot.schemaVersion}`);\n  }\n\n  ensureString(snapshot.adapterId, 'adapterId');\n\n  if (!isObject(snapshot.session)) {\n    throw new Error('Canonical session snapshot requires session to be an object');\n  }\n\n  ensureString(snapshot.session.id, 'session.id');\n  ensureString(snapshot.session.kind, 'session.kind');\n  ensureString(snapshot.session.state, 'session.state');\n  ensureOptionalString(snapshot.session.repoRoot, 'session.repoRoot');\n","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/session-adapters/canonical-session.js#L146-L182","documentation":"validateCanonicalSnapshot is the entry gate for every snapshot write/normalize path (persistCanonicalSnapshot, getFallbackSessionRecordingPath, normalizeDmuxSnapshot, normalizeClaudeHistorySession, normalizeCodexWorktreeSession, normalizeOpencodeSession). This top-of-function check throws when the value passed is not a plain object — null, undefined, an array, a string, or a number. The library treats the snapshot as a structured record and refuses anything else.","triggerScenarios":"Passing null/undefined to persistCanonicalSnapshot; passing a JSON string of the snapshot instead of the parsed object; an upstream function returning undefined on error and its result being forwarded directly; spreading an array where an object was expected.","commonSituations":"Reading a snapshot file that failed to parse and forwarding null; a normalize adapter called with the wrong argument order so the snapshot argument is undefined; forgetting JSON.parse on file contents.","solutions":["JSON.parse file/string contents before calling the persist/normalize function","Check the argument is non-null before calling: if (!snapshot) throw/handle upstream","Verify you are calling the normalize* adapter with the correct argument order","Add a null/undefined guard in the caller that produces the snapshot"],"exampleFix":"// before\npersistCanonicalSnapshot(fs.readFileSync(path, 'utf8'));\n// after\npersistCanonicalSnapshot(JSON.parse(fs.readFileSync(path, 'utf8')));","handlingStrategy":"type-guard","validationCode":"if (!isSnapshotObject(input)) throw new TypeError('Expected snapshot object');","typeGuard":"const isSnapshotObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try { persistCanonicalSnapshot(snap); } catch (e) { if (e.message === 'Canonical session snapshot must be an object') { /* log and regenerate */ } else throw e; }","preventionTips":["Parse JSON before calling","Check producer return values for undefined"],"tags":["validation","schema","null","type-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}