{"record":{"id":"6135814ff536448f","repo":"affaan-m/ECC","slug":"target-harnesses-must-contain-at-least-one-harness","errorCode":null,"errorMessage":"target harnesses must contain at least one harness or \"all\".","messagePattern":"target harnesses must contain at least one harness or \"all\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault-format.js","lineNumber":172,"sourceCode":"  }\n  if (Buffer.byteLength(normalized, 'utf8') > MAX_BODY_BYTES) {\n    throw new Error(`memory body is too large (maximum ${MAX_BODY_BYTES} bytes).`);\n  }\n  return normalized;\n}\n\nfunction normalizeMemory(memory) {\n  if (!memory || typeof memory !== 'object' || Array.isArray(memory)) {\n    throw new Error('memory must be an object.');\n  }\n\n  const targetHarnesses = uniqueStrings(memory.targetHarnesses, {\n    label: 'target harnesses',\n    limit: MAX_TARGETS,\n    validator: value => validateSlug(value, 'target harness'),\n  });\n  if (targetHarnesses.length === 0) {\n    throw new Error('target harnesses must contain at least one harness or \"all\".');\n  }\n\n  if (memory.schema !== MEMORY_SCHEMA_VERSION) {\n    throw new Error('Unsupported memory schema.');\n  }\n\n  return {\n    schema: memory.schema,\n    id: validateMemoryId(memory.id),\n    title: asNonEmptyString(memory.title, 'memory title', MAX_TITLE_CHARS),\n    kind: validateEnum(memory.kind, MEMORY_KINDS, 'memory kind'),\n    scope: validateEnum(memory.scope, MEMORY_SCOPES, 'memory scope'),\n    trust: validateEnum(memory.trust, MEMORY_TRUST_STATES, 'memory trust'),\n    status: validateEnum(memory.status, MEMORY_STATUSES, 'memory status'),\n    sourceHarness: validateSlug(memory.sourceHarness, 'source harness'),\n    targetHarnesses,\n    tags: uniqueStrings(memory.tags, {\n      label: 'tags',","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/memory-vault-format.js#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Default to ['all'] when no specific harness is requested: targetHarnesses: input.targetHarnesses?.length ? input.targetHarnesses : ['all'].","Validate at the form/CLI boundary: refuse to proceed if the user selected zero harnesses.","Prefer saveMemory() which already defaults targetHarnesses to ['all'] when input.targetHarnesses is absent.","If you call normalizeMemory() directly, always set the field explicitly."],"exampleFix":"// before\nsaveMemory({\n  title: 'pref',\n  body: '...'\n  // targetHarnesses omitted entirely in custom path\n});\n\n// after\nsaveMemory({\n  title: 'pref',\n  body: '...',\n  targetHarnesses: input.target?.length ? input.target : ['all']\n});","handlingStrategy":"validation","validationCode":"const targetHarnesses = Array.isArray(input.targetHarnesses) && input.targetHarnesses.length > 0\n  ? input.targetHarnesses\n  : ['all'];\nsaveMemory({ ...input, targetHarnesses });","typeGuard":"function hasValidTargets(value): value is string[] {\n  return Array.isArray(value) && value.length > 0\n    && value.every(v => typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(v));\n}","tryCatchPattern":null,"preventionTips":["Default to ['all'] in your wrapper when the caller supplies no target harnesses.","Build targetHarnesses via filter then assert length > 0.","Let saveMemory() apply its own default by omitting the field entirely."],"tags":["validation","memory-vault","required-field","harness"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}