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

  1. Always pass body as a string literal or string variable: body: String(input.body ?? '').
  2. If body is optional in your wrapper, default it: const body = input.body == null ? '' : String(input.body);
  3. For array input, join explicitly: body: lines.join('\n').
  4. 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

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


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