{"record":{"id":"661899d7cef5a21f","repo":"ruvnet/ruflo","slug":"invalid-claude-flow-encryption-key-expected-32-by","errorCode":null,"errorMessage":"Invalid CLAUDE_FLOW_ENCRYPTION_KEY: expected 32-byte key as 64-char hex or 44-char base64","messagePattern":"Invalid CLAUDE_FLOW_ENCRYPTION_KEY: expected 32-byte key as 64-char hex or 44-char base64","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/encryption/vault.ts","lineNumber":108,"sourceCode":"}\n\n/**\n * Decode a key string. Exposed for testing and for the future passphrase\n * resolver, which will scrypt-derive a Buffer and hand it back through here\n * to share the same length-check.\n */\nexport function decodeKey(raw: string): Buffer {\n  const trimmed = raw.trim();\n  // Hex first — strict 64 chars [0-9a-fA-F]\n  if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {\n    return Buffer.from(trimmed, 'hex');\n  }\n  // Base64 — accept padded 44-char or unpadded 43-char forms\n  if (/^[A-Za-z0-9+/]{43}=?$/.test(trimmed)) {\n    const buf = Buffer.from(trimmed, 'base64');\n    if (buf.length === KEY_LEN) return buf;\n  }\n  throw new Error(\n    `Invalid ${ENV_KEY_VAR}: expected 32-byte key as 64-char hex or 44-char base64`,\n  );\n}\n\n/**\n * Encrypt a plaintext Buffer with AES-256-GCM. Returns the wire-format\n * blob: magic(4) || iv(12) || ciphertext(N) || tag(16).\n *\n * The IV is freshly randomized per call. Reusing a (key, iv) pair under\n * GCM is catastrophic — every call MUST produce a different IV. Node's\n * randomBytes is csprng-backed so this is automatic; the function takes\n * no IV input deliberately.\n */\nexport function encryptBuffer(plaintext: Buffer, key: Buffer): Buffer {\n  if (!Buffer.isBuffer(plaintext)) {\n    throw new TypeError('encryptBuffer: plaintext must be a Buffer');\n  }\n  if (!Buffer.isBuffer(key) || key.length !== KEY_LEN) {","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/encryption/vault.ts#L90-L126","documentation":"Thrown by decodeKey() when CLAUDE_FLOW_ENCRYPTION_KEY does not match the accepted encodings: exactly 64 hex chars [0-9a-fA-F], or base64 of 43 chars plus optional '=' padding (44 total) that decodes to exactly 32 bytes. The vault is strict by design — a truncated or wrongly-encoded key would silently weaken AES-256-GCM, so anything else is rejected loudly. Note base64url characters '-' and '_' are NOT accepted, only '+' and '/'.","triggerScenarios":"A key value with a trailing newline (e.g. `openssl rand -base64 32 > keyfile` then `$(cat keyfile)` — actually trimmed here, but quotes from compose files are not), surrounding quotes injected by YAML/compose interpolation, a base64url-encoded key using - and _, a raw passphrase like 'my-secret-key', a 32-character ASCII string, or a hex key with an 0x prefix (66 chars). decodeKey() trims whitespace first, so the usual culprit is charset or length, not spaces.","commonSituations":"Kubernetes/docker secrets that append a trailing newline or wrap the value in quotes; developers passing a human-chosen passphrase instead of random bytes; copying only part of the key across terminals; using a UUID or `head -c 16` output (wrong byte count); regenerating the key in a different encoding between environments so one env boots and the other throws.","solutions":["Regenerate cleanly: CLAUDE_FLOW_ENCRYPTION_KEY=$(openssl rand -hex 32 | tr -d '\\n') and confirm the value is exactly 64 hex characters","If using base64, ensure it is standard base64 (chars A-Za-z0-9+/) 43-44 chars long — re-encode base64url keys with `tr '_-' '/+'`","Check the value actually delivered to the process: `node -e \"console.log(process.env.CLAUDE_FLOW_ENCRYPTION_KEY.length)\"` must print 64 (hex) or 43/44 (base64)","Remove surrounding quotes/newlines in docker-compose/Kubernetes YAML and verify no `0x` prefix on hex keys"],"exampleFix":"# before — passphrase + base64url chars, decodeKey() throws\nexport CLAUDE_FLOW_ENCRYPTION_KEY=\"my-team-secret-2024\"\n\n# after — exactly 64-char hex (32 bytes)\nexport CLAUDE_FLOW_ENCRYPTION_KEY=\"3f2b8c1e9a4d67f0c5e8b2a19d4f7c6e0b3a58d29f1c47e6b0a3d59c8f21e740\"","handlingStrategy":"validation","validationCode":"function isValidKeyEncoding(raw: string): boolean {\n  const t = raw.trim();\n  return /^[0-9a-fA-F]{64}$/.test(t) || /^[A-Za-z0-9+/]{43}=?$/.test(t);\n}\n\nif (!isValidKeyEncoding(process.env.CLAUDE_FLOW_ENCRYPTION_KEY ?? '')) {\n  throw new Error('CLAUDE_FLOW_ENCRYPTION_KEY must be 64-char hex or 44-char base64');\n}","typeGuard":"const isHexKey = (v: string) => /^[0-9a-fA-F]{64}$/.test(v.trim());\nconst isBase64Key = (v: string) => /^[A-Za-z0-9+/]{43}=?$/.test(v.trim());","tryCatchPattern":null,"preventionTips":["Generate keys only with openssl rand -hex 32 / -base64 32 and never hand-edit them","Assert key length at startup (64 or 43/44 chars) before the first vault call","Beware base64url (- and _) encodings from other tools — re-encode before use","Strip trailing newlines when reading keys from files or mounted secrets"],"tags":["encryption","validation","configuration","base64","hex","key-management"],"backgroundTag":"invalid-encryption-key-format","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}