{"record":{"id":"c99f334e19ec9259","repo":"ruvnet/ruflo","slug":"key-exceeds-maximum-length-of-max-key-length-ch","errorCode":null,"errorMessage":"Key exceeds maximum length of ${MAX_KEY_LENGTH} characters","messagePattern":"Key exceeds maximum length of (.+?) characters","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts","lineNumber":73,"sourceCode":"  if (!existsSync(dir)) {\n    mkdirSync(dir, { recursive: true });\n  }\n}\n\n// D-2: Input bounds for memory parameters\nconst MAX_KEY_LENGTH = 1024;\nconst MAX_VALUE_SIZE = 1024 * 1024; // 1MB\nconst MAX_QUERY_LENGTH = 4096;\n\n// #1425 — single source of truth for the dangerous-character set rejected by\n// validateMemoryInput. Imported by sanitizeMemoryKey so write-side sanitization\n// and read-side rejection can never drift apart (the symmetry bug behind #1884).\nconst DANGEROUS_KEY_CHARS = /[;&|`$(){}[\\]<>!#\\\\\\0]|\\.\\.[/\\\\]/g;\nconst DANGEROUS_KEY_PATTERN = /[;&|`$(){}[\\]<>!#\\\\\\0]|\\.\\.[/\\\\]/;\n\nfunction validateMemoryInput(key?: string, value?: string, query?: string, namespace?: string): void {\n  if (key && key.length > MAX_KEY_LENGTH) {\n    throw new Error(`Key exceeds maximum length of ${MAX_KEY_LENGTH} characters`);\n  }\n  if (value && value.length > MAX_VALUE_SIZE) {\n    throw new Error(`Value exceeds maximum size of ${MAX_VALUE_SIZE} bytes`);\n  }\n  if (query && query.length > MAX_QUERY_LENGTH) {\n    throw new Error(`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);\n  }\n  // Reject path traversal and shell metacharacters in keys/namespaces (#1425)\n  if (key && DANGEROUS_KEY_PATTERN.test(key)) {\n    throw new Error('Key contains disallowed characters');\n  }\n  if (namespace && DANGEROUS_KEY_PATTERN.test(namespace)) {\n    throw new Error('Namespace contains disallowed characters');\n  }\n}\n\n// #1884 — sanitize a key produced from arbitrary input (markdown headings,\n// frontmatter names, file names) so it survives validateMemoryInput on the","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/memory-tools.ts#L55-L91","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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, ..."],"exampleFix":"// before\nconst key = `report-${filePath}-${JSON.stringify(params)}`; // can exceed 1024 chars\nawait mcp.callTool('memory_store', { key, value: summary }); // Key exceeds maximum length\n\n// after\nconst key = `report-${createHash('sha256').update(filePath + JSON.stringify(params)).digest('hex').slice(0, 32)}`;\nawait mcp.callTool('memory_store', { key, value: summary, }); // metadata kept in value/tags","handlingStrategy":"validation","validationCode":"const MAX_KEY_LENGTH = 1024;\nfunction prepareMemoryKey(rawKey: string): string {\n  let key = rawKey.replace(/[;&|`$(){}[\\]<>!#\\\\\\0]|\\.\\.[/\\\\]/g, '_');\n  if (key.length > MAX_KEY_LENGTH) key = key.slice(0, MAX_KEY_LENGTH);\n  return key;\n}\n// const key = prepareMemoryKey(generatedKey); // always <= 1024 and charset-safe","typeGuard":null,"tryCatchPattern":"try {\n  await memoryStore({ key, value });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Key exceeds maximum length')) {\n    // deterministic: shorten/hash the key, then retry once with the compact form\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["memory","mcp","validation","limits","key"],"backgroundTag":"input-length-limit-exceeded","analyzedSha":"9c61c86f06b439af2a95085ae9bb0ca839662e41","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}