ruvnet/ruflo · error · Error
Key exceeds maximum length of
Error message
Key exceeds maximum length of ${MAX_KEY_LENGTH} characters What it means
memory-tools enforces a hard key ceiling: validateMemoryInput throws when key.length exceeds MAX_KEY_LENGTH (1024 characters). The check guards both the write path (memory_store) and the read/delete paths that route through validateMemoryInput, keeping the underlying AgentDB/SQLite backend free of pathological keys. The plain Error propagates out of the tool handler as a failed call; nothing is stored or read.
Solutions
- Shorten the key to 1024 characters or fewer and move distinguishing detail into the value or tags
- Derive a stable compact key — crypto.createHash('sha256').update(longName).digest('hex').slice(0, 32) — and keep the original name inside the value
- Replicate the library's own truncation: key.slice(0, 1024) after sanitizing dangerous characters
- If the key is legitimately long, split one logical entry into several entries keyed doc-1, doc-2, ...
Example fix
// before
const key = `report-${filePath}-${JSON.stringify(params)}`; // can exceed 1024 chars
await mcp.callTool('memory_store', { key, value: summary }); // Key exceeds maximum length
// after
const key = `report-${createHash('sha256').update(filePath + JSON.stringify(params)).digest('hex').slice(0, 32)}`;
await mcp.callTool('memory_store', { key, value: summary, }); // metadata kept in value/tags Defensive patterns
Strategy: validation
Validate before calling
const MAX_KEY_LENGTH = 1024;
function prepareMemoryKey(rawKey: string): string {
let key = rawKey.replace(/[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/g, '_');
if (key.length > MAX_KEY_LENGTH) key = key.slice(0, MAX_KEY_LENGTH);
return key;
}
// const key = prepareMemoryKey(generatedKey); // always <= 1024 and charset-safe Try / catch
try {
await memoryStore({ key, value });
} catch (e) {
if (e instanceof Error && e.message.includes('Key exceeds maximum length')) {
// deterministic: shorten/hash the key, then retry once with the compact form
}
throw e;
} Prevention
- Route every generated key through a truncate-after-sanitize helper (mirror sanitizeMemoryKey) before storage
- Hash long source names (sha256, first 32 hex chars) instead of embedding them in keys
- Keep discriminating metadata in values/tags, not keys
- Assert key.length <= 1024 in your test fixtures for key-generation code
When it happens
Trigger: memory_store with a key assembled from a full file path, a long generated slug, a base64/hex digest, or concatenated identifiers that together exceed 1024 characters; memory_retrieve or memory_delete with the same oversized key (it will fail the same way even if nothing was ever stored under it).
Common situations: Auto-generating keys from markdown headings, file names, or log lines without truncation (the codebase ships sanitizeMemoryKey for exactly this); embedding timestamps, UUIDs, and paths into keys; LLM-authored keys that paste an entire sentence or stack frame.
Related errors
- Key contains disallowed characters
- namespace exceeds 128 characters
- Query exceeds maximum length of
- Value exceeds maximum size of
- label exceeds 256 chars
AI-assisted analysis of ruvnet/ruflo@9c61c86f06 (2026-08-18).
Data as JSON: /api/errors/c99f334e19ec9259.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts:73
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
// D-2: Input bounds for memory parameters
const MAX_KEY_LENGTH = 1024;
const MAX_VALUE_SIZE = 1024 * 1024; // 1MB
const MAX_QUERY_LENGTH = 4096;
// #1425 — single source of truth for the dangerous-character set rejected by
// validateMemoryInput. Imported by sanitizeMemoryKey so write-side sanitization
// and read-side rejection can never drift apart (the symmetry bug behind #1884).
const DANGEROUS_KEY_CHARS = /[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/g;
const DANGEROUS_KEY_PATTERN = /[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/;
function validateMemoryInput(key?: string, value?: string, query?: string, namespace?: string): void {
if (key && key.length > MAX_KEY_LENGTH) {
throw new Error(`Key exceeds maximum length of ${MAX_KEY_LENGTH} characters`);
}
if (value && value.length > MAX_VALUE_SIZE) {
throw new Error(`Value exceeds maximum size of ${MAX_VALUE_SIZE} bytes`);
}
if (query && query.length > MAX_QUERY_LENGTH) {
throw new Error(`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);
}
// Reject path traversal and shell metacharacters in keys/namespaces (#1425)
if (key && DANGEROUS_KEY_PATTERN.test(key)) {
throw new Error('Key contains disallowed characters');
}
if (namespace && DANGEROUS_KEY_PATTERN.test(namespace)) {
throw new Error('Namespace contains disallowed characters');
}
}
// #1884 — sanitize a key produced from arbitrary input (markdown headings,
// frontmatter names, file names) so it survives validateMemoryInput on theView on GitHub (pinned to 9c61c86f06)