affaan-m/ECC · error · Error
memory body must be a string.
Error message
memory body must be a string.
What it means
Thrown by normalizeBody() as its very first check: memory.body must be typeof 'string'. The body field is the human-readable context of a memory entry; non-string values (numbers, booleans, arrays, plain objects, null) cannot be serialized into the markdown body section, so they are rejected up front rather than stringified. This runs before the empty/size/character checks.
Source
Thrown at scripts/lib/memory-vault-format.js:146
}, []);
}
function validateTimestamp(value, label) {
const normalized = asNonEmptyString(value, label, 64);
const parsed = new Date(normalized);
if (
!ISO_TIMESTAMP_PATTERN.test(normalized)
|| Number.isNaN(parsed.getTime())
|| parsed.toISOString() !== normalized
) {
throw new Error(`${label} must be an ISO-8601 timestamp.`);
}
return normalized;
}
function normalizeBody(value) {
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.');
}View on GitHub (pinned to 01e15490f0)
Solutions
- Always pass body as a string literal or string variable: body: String(input.body ?? '').
- If body is optional in your wrapper, default it: const body = input.body == null ? '' : String(input.body);
- For array input, join explicitly: body: lines.join('\n').
- Add a TypeScript type { body: string } on the input shape so the mistake surfaces at compile time.
Example fix
// before
saveMemory({ title: 'note', body: req.json.body }); // body parsed as null/number
// after
saveMemory({
title: 'note',
body: typeof req.json.body === 'string' ? req.json.body : ''
}); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof input.body !== 'string') {
throw new TypeError('input.body must be a string; got ' + typeof input.body);
}
saveMemory(input); Type guard
function isMemoryBody(value): value is string {
return typeof value === 'string';
} Try / catch
try {
saveMemory(input);
} catch (err) {
if (/memory body must be a string/.test(err.message)) {
saveMemory({ ...input, body: String(input.body ?? '') });
} else throw err;
} Prevention
- Type the saveMemory input as { body: string } in TypeScript.
- Default at the boundary: const body = typeof input.body === 'string' ? input.body : '';
- Never destructure body out of an untyped JSON.parse result without re-checking its type.
When it happens
Trigger: saveMemory({body: null}) when the caller treats absent body as null, saveMemory({body: 42}) or saveMemory({body: {note: 'x'}}) from untyped input, saveMemory({body: ['line1','line2']}) assuming arrays are joined, or normalizeMemory({body: undefined}) when a destructuring step dropped the field.
Common situations: Calling saveMemory with a partially-built object from a form or CLI arg parser that leaves body unset. Passing through JSON.parse() output where body was missing and became undefined. Refactoring that changed body from string to {text, format} object without updating the call site.
Related errors
- memory body must contain non-whitespace context.
- memory must be an object.
- Memory roots must include a trusted boundary policy.
- ${label} must be a non-empty string.
- ${label} is too long (maximum ${maxChars} characters).
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/2935e60c1499653d.
Report an issue: GitHub.