affaan-m/ECC · error · Error

target harnesses must contain at least one harness or "all".

Error message

target harnesses must contain at least one harness or "all".

What it means

Thrown by normalizeMemory() after uniqueStrings() succeeds when memory.targetHarnesses.length === 0. Each memory must declare which agent harnesses should see it (e.g. 'claude', 'codex', 'cursor') or use the literal 'all'. An empty array would make the memory invisible to every consumer and is therefore rejected as a programming mistake rather than silently stored.

Source

Thrown at scripts/lib/memory-vault-format.js:172

  }
  if (Buffer.byteLength(normalized, 'utf8') > MAX_BODY_BYTES) {
    throw new Error(`memory body is too large (maximum ${MAX_BODY_BYTES} bytes).`);
  }
  return normalized;
}

function normalizeMemory(memory) {
  if (!memory || typeof memory !== 'object' || Array.isArray(memory)) {
    throw new Error('memory must be an object.');
  }

  const targetHarnesses = uniqueStrings(memory.targetHarnesses, {
    label: 'target harnesses',
    limit: MAX_TARGETS,
    validator: value => validateSlug(value, 'target harness'),
  });
  if (targetHarnesses.length === 0) {
    throw new Error('target harnesses must contain at least one harness or "all".');
  }

  if (memory.schema !== MEMORY_SCHEMA_VERSION) {
    throw new Error('Unsupported memory schema.');
  }

  return {
    schema: memory.schema,
    id: validateMemoryId(memory.id),
    title: asNonEmptyString(memory.title, 'memory title', MAX_TITLE_CHARS),
    kind: validateEnum(memory.kind, MEMORY_KINDS, 'memory kind'),
    scope: validateEnum(memory.scope, MEMORY_SCOPES, 'memory scope'),
    trust: validateEnum(memory.trust, MEMORY_TRUST_STATES, 'memory trust'),
    status: validateEnum(memory.status, MEMORY_STATUSES, 'memory status'),
    sourceHarness: validateSlug(memory.sourceHarness, 'source harness'),
    targetHarnesses,
    tags: uniqueStrings(memory.tags, {
      label: 'tags',

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Default to ['all'] when no specific harness is requested: targetHarnesses: input.targetHarnesses?.length ? input.targetHarnesses : ['all'].
  2. Validate at the form/CLI boundary: refuse to proceed if the user selected zero harnesses.
  3. Prefer saveMemory() which already defaults targetHarnesses to ['all'] when input.targetHarnesses is absent.
  4. If you call normalizeMemory() directly, always set the field explicitly.

Example fix

// before
saveMemory({
  title: 'pref',
  body: '...'
  // targetHarnesses omitted entirely in custom path
});

// after
saveMemory({
  title: 'pref',
  body: '...',
  targetHarnesses: input.target?.length ? input.target : ['all']
});
Defensive patterns

Strategy: validation

Validate before calling

const targetHarnesses = Array.isArray(input.targetHarnesses) && input.targetHarnesses.length > 0
  ? input.targetHarnesses
  : ['all'];
saveMemory({ ...input, targetHarnesses });

Type guard

function hasValidTargets(value): value is string[] {
  return Array.isArray(value) && value.length > 0
    && value.every(v => typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(v));
}

Prevention

When it happens

Trigger: saveMemory({targetHarnesses: []}), saveMemory({}) when the caller omitted targetHarnesses and the defaulting layer did not supply ['all'], or saveMemory({targetHarnesses: undefined}) where normalizeSaveInput did NOT apply its default because input was passed through unchanged.

Common situations: Wrapper that builds targetHarnesses from a filter without checking the result is non-empty. CLI that takes --target as a repeatable flag but received zero repetitions. Migration script that copies targetHarnesses from an old schema where the field was optional.

Related errors


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