affaan-m/ECC · error · Error

Canonical session snapshot requires workers

Error message

Canonical session snapshot requires workers[${index}] to be an object

What it means

This error is thrown by validateCanonicalSnapshot in scripts/lib/session-adapters/canonical-session.js when a canonical session snapshot's workers array contains a non-object entry. The validator walks every element of snapshot.workers and enforces the canonical snapshot schema before the snapshot is persisted or normalized. It guarantees downstream consumers can safely read worker.id, label, state, and health.

Solutions

  1. Inspect the snapshot's workers array at the reported index and replace the non-object entry with a full worker object containing id, label, state, health, runtime, intent, outputs, and artifacts.
  2. Fix the upstream adapter or builder that produced the workers array so every entry is a plain object.
  3. Validate the snapshot with validateCanonicalSnapshot (or a schema tool) immediately after construction to catch malformed workers before persisting.
  4. If the snapshot came from a file, re-generate it with the current session-manager version rather than patching stale JSON.

Example fix

// before
const snapshot = { workers: ['worker-a'] };
persistCanonicalSnapshot(snapshot);
// after
const snapshot = {
  workers: [{ id: 'worker-a', label: 'Worker A', state: 'idle', health: 'healthy',
    runtime: { kind: 'dmux', active: true, dead: false },
    intent: { objective: '', seedPaths: [] },
    outputs: { summary: [], validation: [], remainingRisks: [] },
    artifacts: {} }]
};
persistCanonicalSnapshot(snapshot);
Defensive patterns

Strategy: validation

Validate before calling

function isValidWorker(w) { return !!w && typeof w === 'object' && !Array.isArray(w); }
if (!Array.isArray(snapshot.workers) || !snapshot.workers.every(isValidWorker)) throw new Error('snapshot.workers must be an array of objects');

Type guard

function isWorkerObject(w) { return typeof w === 'object' && w !== null && !Array.isArray(w) && typeof w.id === 'string' && typeof w.label === 'string' && typeof w.state === 'string' && typeof w.health === 'string'; }

Try / catch

try { persistCanonicalSnapshot(snapshot); } catch (err) { if (String(err.message).includes('workers[')) { console.error('Malformed worker entry in snapshot:', err.message); /* rebuild or regenerate snapshot */ } else { throw err; } }

Prevention

When it happens

Trigger: Calling persistCanonicalSnapshot or any of the normalize*Session/normalizeDmuxSnapshot adapters with a snapshot whose workers array contains null, undefined, a primitive, or an array element instead of a worker object.

Common situations: Adapters built from stale schemas that still emit string or null worker entries; hand-edited or externally generated snapshot JSON loaded and passed to persistCanonicalSnapshot; a mapping step that filters workers but leaves sparse array holes (delete on array elements).

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/c3eaf1c7886760d0. Report an issue: GitHub.

Appendix: source

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

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

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

  ensureString(snapshot.session.sourceTarget.type, 'session.sourceTarget.type');
  ensureString(snapshot.session.sourceTarget.value, 'session.sourceTarget.value');

  if (!Array.isArray(snapshot.workers)) {
    throw new Error('Canonical session snapshot requires workers to be an array');
  }

  snapshot.workers.forEach((worker, index) => {
    if (!isObject(worker)) {
      throw new Error(`Canonical session snapshot requires workers[${index}] to be an object`);
    }

    ensureString(worker.id, `workers[${index}].id`);
    ensureString(worker.label, `workers[${index}].label`);
    ensureString(worker.state, `workers[${index}].state`);
    ensureString(worker.health, `workers[${index}].health`);
    ensureOptionalString(worker.branch, `workers[${index}].branch`);
    ensureOptionalString(worker.worktree, `workers[${index}].worktree`);

    if (!isObject(worker.runtime)) {
      throw new Error(`Canonical session snapshot requires workers[${index}].runtime to be an object`);
    }

    ensureString(worker.runtime.kind, `workers[${index}].runtime.kind`);
    ensureOptionalString(worker.runtime.command, `workers[${index}].runtime.command`);
    ensureBoolean(worker.runtime.active, `workers[${index}].runtime.active`);
    ensureBoolean(worker.runtime.dead, `workers[${index}].runtime.dead`);

View on GitHub (pinned to 8321021c54)