mastra-ai/mastra · critical

${name} must contain base64-encoded 32-byte keys.

Error message

${name} must contain base64-encoded 32-byte keys.

What it means

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.

Source

Thrown at mastracode/web/src/mastra/index.ts:56

import { SlackIntegration } from '@mastra/factory/integrations/slack/integration';
import type { IMastraAuthProvider } from '@mastra/core/server';

/**
 * Parse a positive-integer env knob; anything else means "use the default".
 * Fractional values are rejected rather than floored — flooring `0.5` to `0`
 * would silently disable a capacity knob or turn an idle window into
 * immediate expiry.
 */
function positiveInt(raw: string | undefined): number | undefined {
  if (!raw) return undefined;
  const parsed = Number(raw);
  if (!Number.isSafeInteger(parsed) || parsed <= 0) return undefined;
  return parsed;
}

function decodeCredentialEncryptionKey(name: string, encodedKey: string): Buffer {
  const key = Buffer.from(encodedKey, 'base64');
  if (key.byteLength !== 32) throw new Error(`${name} must contain base64-encoded 32-byte keys.`);
  return key;
}

function credentialEncryption() {
  const encodedKey = process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY?.trim();
  if (!encodedKey) {
    console.warn(
      '[factory] FACTORY_CREDENTIAL_ENCRYPTION_KEY is not set. Stored model-provider keys, custom-provider ' +
        'API keys, and integration secrets will be persisted as plaintext. Generate a key with ' +
        '`openssl rand -base64 32` and set FACTORY_CREDENTIAL_ENCRYPTION_KEY to encrypt them at rest.',
    );
    return undefined;
  }

  const previousKeys: Record<string, unknown> = process.env.FACTORY_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS
    ? JSON.parse(process.env.FACTORY_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS)
    : {};
  if (!previousKeys || Array.isArray(previousKeys) || typeof previousKeys !== 'object') {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Regenerate the key as exactly 32 bytes and base64 it: `openssl rand -base64 32`
  2. Verify with `node -e "console.log(Buffer.from(process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY,'base64').byteLength)"` that it prints 32
  3. If a hex key was used, convert: Buffer.from(hexKey,'hex').toString('base64')
  4. 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
  5. Fix quoting in the env file so the base64 value (which may contain +/=) is preserved exactly

Example fix

// before
FACTORY_CREDENTIAL_ENCRYPTION_KEY=my-secret-passphrase
// after
FACTORY_CREDENTIAL_ENCRYPTION_KEY=$(openssl rand -base64 32)  # 44 chars, decodes to 32 bytes
Defensive patterns

Strategy: validation

Validate before calling

const key = process.env.FACTORY_CREDENTIAL_ENCRYPTION_KEY;
if (!key || Buffer.from(key.trim(), 'base64').byteLength !== 32) {
  throw new Error('FACTORY_CREDENTIAL_ENCRYPTION_KEY must be base64 of exactly 32 bytes (generate: openssl rand -base64 32)');
}

Type guard

function isEncryptionKey(v: unknown): v is string {
  return typeof v === 'string' && v.trim().length > 0 && Buffer.from(v.trim(), 'base64').byteLength === 32;
}

Try / catch

try {
  startServer();
} catch (e) {
  if (String(e.message).includes('must contain base64-encoded 32-byte keys')) {
    console.error('Bad encryption key; regenerate with: openssl rand -base64 32');
    process.exit(1);
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/107f2e5848f0efb6. Report an issue: GitHub.