affaan-m/ECC · error · Error
memory body must not contain unsafe control or bidirectional
Error message
memory body must not contain unsafe control or bidirectional formatting characters.
What it means
Thrown by normalizeBody() via hasUnsafeControlCharacters(value, true) when the body contains C0 control bytes (U+0000–U+001F except TAB/LF/CR), DEL/C1 range (U+007F–U+009F), or Unicode bidirectional formatting characters (U+202A–U+202E RLE/LRE/RLO/LRO/PDF, U+2066–U+2069 LRI/RLI/FSI/PDI). These characters enable trojan-source attacks and terminal/log injection, so they are rejected even when the rest of the body is valid text.
Source
Thrown at scripts/lib/memory-vault-format.js:149
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.');
}
const targetHarnesses = uniqueStrings(memory.targetHarnesses, {
label: 'target harnesses',View on GitHub (pinned to 01e15490f0)
Solutions
- Strip control/bidi characters before saving: body = body.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069]/g, '').
- Whitelist printable text: keep only /^\u0009\u000A\u000D\u0020-\uFFFF$/u per character.
- If you genuinely need ANSI escapes, base64-encode that section first.
- Run the input through a sanitiser like the library's exported hasUnsafeControlCharacters() to detect issues before calling saveMemory.
Example fix
// before
saveMemory({ title: 'log', body: rawCliCapture }); // contains \x1b[31m red escapes
// after
const { hasUnsafeControlCharacters } = require('./scripts/lib/memory-vault-format');
const clean = rawCliCapture.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069]/g, '');
if (hasUnsafeControlCharacters(clean, true)) throw new Error('could not sanitize');
saveMemory({ title: 'log', body: clean }); Defensive patterns
Strategy: validation
Validate before calling
const { hasUnsafeControlCharacters } = require('./scripts/lib/memory-vault-format');
if (hasUnsafeControlCharacters(input.body, true)) {
input.body = input.body.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069]/g, '');
}
saveMemory(input); Type guard
import { hasUnsafeControlCharacters } from './scripts/lib/memory-vault-format';
function isSafeBody(value): value is string {
return typeof value === 'string' && !hasUnsafeControlCharacters(value, true);
} Prevention
- Treat any paste from terminals, browsers, or rich-text editors as untrusted; sanitise before saving.
- Run hasUnsafeControlCharacters() on incoming text and reject or strip before reaching saveMemory().
- Ban binary/base64 blobs in body — store them out-of-band and reference by path.
When it happens
Trigger: Pasting memory content from a terminal capture that includes ANSI escape sequences (\x1b[31m). Copying source code that contains embedded zero-width or RTL override characters. Body scraped from a webpage with bidirectional markup. Body that includes a literal form-feed (\f) or vertical tab (\v).
Common situations: User pastes rich text from a word processor that smuggles in U+200E/U+200F marks. Build tool emits progress bars with carriage-return/backspace into a captured log that gets saved as a memory. AI agent saves a code snippet that itself contains obfuscated unicode to evade detection.
Related errors
- ${label} must not contain control or bidirectional formattin
- No trusted boundary policy is configured for memory scope "$
- ${label} must be a regular, non-symlink file.
- Project memory .gitignore does not contain the required fail
- Refusing to save memory containing a suspected secret (${sec
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/8cc0edf1edd83b9f.
Report an issue: GitHub.