affaan-m/ECC · error · Error

Canonical session snapshot requires workers[${index}].intent

Error message

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

What it means

worker.intent is missing or not a plain object. The schema requires worker.intent to hold objective (string, may be empty) and seedPaths (array of strings). This fires before those field checks, so any non-object intent is rejected outright.

Source

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

    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`);

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

    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`);
    }
  });

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Always set worker.intent = { objective: <string>, seedPaths: <string[]> } even when empty: { objective: '', seedPaths: [] }.
  2. Follow normalizeClaudeHistorySession's pattern: objective from metadata title or inProgress[0], seedPaths via parseContextSeedPaths(metadata.context).
  3. If loading old recordings, run a migration that nests legacy worker.objective under worker.intent.objective.
  4. Add a fixture-based unit test that builds the minimal worker and runs validateCanonicalSnapshot to catch the omission early.

Example fix

// before
const worker = { id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...}, outputs:{...}, artifacts:{...} };

// after
const worker = {
  id:'w1', label:'w1', state:'recorded', health:'healthy', runtime:{...},
  intent: { objective: metadata.title || '', seedPaths: parseContextSeedPaths(metadata.context) },
  outputs:{...}, artifacts:{...}
};
Defensive patterns

Strategy: type-guard

Validate before calling

function ensureWorkerIntent(worker) {
  if (!worker.intent || typeof worker.intent !== 'object') {
    worker.intent = { objective: '', seedPaths: [] };
  }
  return worker;
}

Type guard

function hasValidIntent(w) {
  const i = w.intent;
  return i !== null && typeof i === 'object' && !Array.isArray(i)
    && typeof i.objective === 'string' && Array.isArray(i.seedPaths);
}

Try / catch

try { validateCanonicalSnapshot(snapshot); }
catch (err) {
  if (err.message.includes('.intent to be an object')) {
    snapshot.workers.forEach(w => { if (!hasValidIntent(w)) ensureWorkerIntent(w); });
    validateCanonicalSnapshot(snapshot);
  } else throw err;
}

Prevention

When it happens

Trigger: Custom adapter constructs a worker but omits intent. Reading a persisted recording written by an adapter version that put the objective at worker.objective instead of worker.intent.objective. Hand-editing a JSON recording and removing the intent block.

Common situations: Adapting a new session source where the task objective is optional and the adapter author skips intent. Migrating snapshots between schema versions without remapping fields. Test fixtures that build minimal worker objects.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/2a759830be7dcae6. Report an issue: GitHub.