{"record":{"id":"107f2e5848f0efb6","repo":"mastra-ai/mastra","slug":"name-must-contain-base64-encoded-32-byte-keys","errorCode":null,"errorMessage":"${name} must contain base64-encoded 32-byte keys.","messagePattern":"(.+?) must contain base64-encoded 32-byte keys\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"mastracode/web/src/mastra/index.ts","lineNumber":56,"sourceCode":"import { SlackIntegration } from '@mastra/factory/integrations/slack/integration';\nimport type { IMastraAuthProvider } from '@mastra/core/server';\n\n/**\n * Parse a positive-integer env knob; anything else means \"use the default\".\n * Fractional values are rejected rather than floored — flooring `0.5` to `0`\n * would silently disable a capacity knob or turn an idle window into\n * immediate expiry.\n */\nfunction positiveInt(raw: string | undefined): number | undefined {\n  if (!raw) return undefined;\n  const parsed = Number(raw);\n  if (!Number.isSafeInteger(parsed) || parsed <= 0) return undefined;\n  return parsed;\n}\n\nfunction decodeCredentialEncryptionKey(name: string, encodedKey: string): Buffer {\n  const key = Buffer.from(encodedKey, 'base64');\n  if (key.byteLength !== 32) throw new Error(`${name} must contain base64-encoded 32-byte keys.`);\n  return key;\n}\n\nfunction credentialEncryption() {\n  const encodedKey = process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY?.trim();\n  if (!encodedKey) {\n    console.warn(\n      '[factory] FACTORY_CREDENTIAL_ENCRYPTION_KEY is not set. Stored model-provider keys, custom-provider ' +\n        'API keys, and integration secrets will be persisted as plaintext. Generate a key with ' +\n        '`openssl rand -base64 32` and set FACTORY_CREDENTIAL_ENCRYPTION_KEY to encrypt them at rest.',\n    );\n    return undefined;\n  }\n\n  const previousKeys: Record<string, unknown> = process.env.FACTORY_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS\n    ? JSON.parse(process.env.FACTORY_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS)\n    : {};\n  if (!previousKeys || Array.isArray(previousKeys) || typeof previousKeys !== 'object') {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/web/src/mastra/index.ts#L38-L74","documentation":"decodeCredentialEncryptionKey expects FACTORY_CREDENTIAL_ENCRYPTION_KEY (or a previous key) to be a base64 string that decodes to exactly 32 bytes, suitable for AES-256. If Buffer.from(encodedKey, 'base64') yields any other length, it throws '<name> must contain base64-encoded 32-byte keys.' This guards against silently using a weak or malformed encryption key for stored credentials.","triggerScenarios":"Starting the mastra web server with FACTORY_CREDENTIAL_ENCRYPTION_KEY set to a value that is not valid base64 or does not decode to 32 bytes — e.g. a raw passphrase, a hex string, a truncated key, or a base64 of a 16- or 64-byte secret.","commonSituations":"Generating the key with something other than 32 random bytes (e.g. `openssl rand -hex 32`, which yields 64 base64-decoded bytes, or `head -c 16`), copying the key with padding/newline issues, or pasting a password instead of a generated key.","solutions":["Regenerate the key as exactly 32 bytes and base64 it: `openssl rand -base64 32`","Verify with `node -e \"console.log(Buffer.from(process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY,'base64').byteLength)\"` that it prints 32","If a hex key was used, convert: Buffer.from(hexKey,'hex').toString('base64')","Move the old unusable key into FACTORY_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS only if it also satisfies the 32-byte rule, otherwise decrypt-and-reencrypt credentials with a valid key","Fix quoting in the env file so the base64 value (which may contain +/=) is preserved exactly"],"exampleFix":"// before\nFACTORY_CREDENTIAL_ENCRYPTION_KEY=my-secret-passphrase\n// after\nFACTORY_CREDENTIAL_ENCRYPTION_KEY=$(openssl rand -base64 32)  # 44 chars, decodes to 32 bytes","handlingStrategy":"validation","validationCode":"const key = process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY;\nif (!key || Buffer.from(key.trim(), 'base64').byteLength !== 32) {\n  throw new Error('FACTORY_CREDENTIAL_ENCRYPTION_KEY must be base64 of exactly 32 bytes (generate: openssl rand -base64 32)');\n}","typeGuard":"function isEncryptionKey(v: unknown): v is string {\n  return typeof v === 'string' && v.trim().length > 0 && Buffer.from(v.trim(), 'base64').byteLength === 32;\n}","tryCatchPattern":"try {\n  startServer();\n} catch (e) {\n  if (String(e.message).includes('must contain base64-encoded 32-byte keys')) {\n    console.error('Bad encryption key; regenerate with: openssl rand -base64 32');\n    process.exit(1);\n  }\n}","preventionTips":["Generate keys only with `openssl rand -base64 32`","Add a CI/startup check that decodes key env vars to 32 bytes","Never paste passphrases or hex strings as base64 keys","Quote env values containing + or / in dotenv files"],"tags":["configuration","encryption","env-var","security"],"backgroundTag":"invalid-encryption-key","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}