affaan-m/ECC · error · Error
memory must be an object.
Error message
memory must be an object.
What it means
normalizeMemory() validates that the memory argument passed to save/normalize is a plain, non-null, non-array object before applying field-level validation. The memory vault format requires an object mapping named fields (schema, id, title, targetHarnesses, etc.), so anything else — null, undefined, strings, arrays — is rejected early with this clear message instead of producing cryptic failures downstream.
Solutions
- Wrap the memory payload in an object literal with the required fields (schema, id, title, targetHarnesses, ...).
- Check the input source: if it came from JSON.parse, confirm the top-level value is an object, not an array or scalar.
- Add a guard before the call: if (!memory || typeof memory !== 'object' || Array.isArray(memory)) handle the error case.
- If using `null` as a sentinel for 'no memory', branch before calling normalize instead of passing null through.
Example fix
// before
const memory = await loadMemory();
await saveMemory(memory); // loadMemory() may return null
// after
const memory = await loadMemory() ?? { schema: MEMORY_SCHEMA_VERSION, id: 'default', title: 'Untitled', targetHarnesses: ['all'] };
if (memory && typeof memory === 'object' && !Array.isArray(memory)) {
await saveMemory(memory);
} Defensive patterns
Strategy: type-guard
Validate before calling
function isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }
if (!isPlainObject(input)) throw new TypeError('memory payload must be a plain object'); Type guard
const isMemoryInput = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
Try / catch
try {
const memory = normalizeMemory(input);
} catch (err) {
if (err.message === 'memory must be an object.') {
console.error('Refusing to save: memory payload is not an object (got %s).', input === null ? 'null' : typeof input);
} else throw err;
} Prevention
- Validate JSON.parse output shape before passing it to the memory API.
- Never pass `null` as a default memory value; branch before the call.
- Check that file/stdin sources parse to an object, not an array or scalar.
When it happens
Trigger: Calling normalizeMemory (directly or via normalizeSaveInput / parseMemoryDocument callers / normalized) with null, undefined, a string, a number, an array (e.g. JSON.parse('[...]')), or a Map/Set instead of a plain object.
Common situations: Piping JSON output from another tool where the top level is an array; forgetting to await a promise so a non-object flows in; passing null as a default; destructuring mistakes that yield undefined; scripts that read an empty file and pass '' as the memory.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Canonical session snapshot must be an object
- Canonical session snapshot requires
- Canonical session snapshot requires
- Canonical session snapshot requires
- explicit opacity must be finite within 0..100
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/437fec94758fd1a2.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/memory-vault-format.js:163
if (typeof value !== 'string') {
throw new Error('memory body must be a string.');
}
if (hasUnsafeControlCharacters(value, true)) {
throw new Error('memory body must not contain unsafe control or bidirectional formatting characters.');
}
const normalized = value.trim();
if (normalized.length === 0) {
throw new Error('memory body must contain non-whitespace context.');
}
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),View on GitHub (pinned to 8321021c54)