affaan-m/ECC · error

Canonical session snapshot must be an object

Error message

Canonical session snapshot must be an object

What it means

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.

Solutions

  1. JSON.parse file/string contents before calling the persist/normalize function
  2. Check the argument is non-null before calling: if (!snapshot) throw/handle upstream
  3. Verify you are calling the normalize* adapter with the correct argument order
  4. Add a null/undefined guard in the caller that produces the snapshot

Example fix

// before
persistCanonicalSnapshot(fs.readFileSync(path, 'utf8'));
// after
persistCanonicalSnapshot(JSON.parse(fs.readFileSync(path, 'utf8')));
Defensive patterns

Strategy: type-guard

Validate before calling

if (!isSnapshotObject(input)) throw new TypeError('Expected snapshot object');

Type guard

const isSnapshotObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);

Try / catch

try { persistCanonicalSnapshot(snap); } catch (e) { if (e.message === 'Canonical session snapshot must be an object') { /* log and regenerate */ } else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/c57fa26dae80a1de. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/session-adapters/canonical-session.js:164

  const failedCount = (workerStates.failed || 0) + (workerStates.error || 0);
  if (failedCount > 0) {
    return 'failed';
  }

  const completedCount = (workerStates.completed || 0)
    + (workerStates.succeeded || 0)
    + (workerStates.success || 0)
    + (workerStates.done || 0);
  if (completedCount === totalWorkers) {
    return 'completed';
  }

  return 'idle';
}

function validateCanonicalSnapshot(snapshot) {
  if (!isObject(snapshot)) {
    throw new Error('Canonical session snapshot must be an object');
  }

  ensureString(snapshot.schemaVersion, 'schemaVersion');
  if (snapshot.schemaVersion !== SESSION_SCHEMA_VERSION) {
    throw new Error(`Unsupported canonical session schema version: ${snapshot.schemaVersion}`);
  }

  ensureString(snapshot.adapterId, 'adapterId');

  if (!isObject(snapshot.session)) {
    throw new Error('Canonical session snapshot requires session to be an object');
  }

  ensureString(snapshot.session.id, 'session.id');
  ensureString(snapshot.session.kind, 'session.kind');
  ensureString(snapshot.session.state, 'session.state');
  ensureOptionalString(snapshot.session.repoRoot, 'session.repoRoot');

View on GitHub (pinned to 8321021c54)