ruvnet/ruflo · error · Error

CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTIO

Error message

CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY is not. Provide a 32-byte key as 64-char hex or 44-char base64. See ADR-096 for keychain/passphrase support (coming in a follow-up).

What it means

Thrown by getKey() in the encryption vault when the at-rest encryption feature flag CLAUDE_FLOW_ENCRYPT_AT_REST is present in the environment but CLAUDE_FLOW_ENCRYPTION_KEY is not. The vault fails fast at key-load time rather than silently skipping encryption or deriving a weak key. The key must encode exactly 32 bytes (AES-256) as 64-char hex or 44-char base64; passphrase/keychain support (ADR-096) is not yet implemented.

Source

Thrown at v3/@claude-flow/cli/src/encryption/vault.ts:83

 * Resolve a 32-byte encryption key from CLAUDE_FLOW_ENCRYPTION_KEY.
 *
 * Phase 1 supports only the env-var source; keychain and passphrase
 * resolution are deferred to a follow-up iteration (see ADR-096). When
 * encryption is enabled but no key resolves, this throws with a clear
 * message rather than silently falling back to plaintext (fail-closed).
 *
 * Accepted encodings (auto-detected by length):
 *   - 64-char hex (32 bytes)
 *   - 44-char base64 (32 bytes + padding)
 *   - exactly 32 raw bytes (rare; for callers that pre-decode)
 *
 * Anything else is rejected — we'd rather fail loudly than encrypt with a
 * truncated key.
 */
export function getKey(): Buffer {
  const raw = process.env[ENV_KEY_VAR];
  if (!raw) {
    throw new Error(
      `${ENV_ENABLE_FLAG} is set but ${ENV_KEY_VAR} is not. ` +
      `Provide a 32-byte key as 64-char hex or 44-char base64. ` +
      `See ADR-096 for keychain/passphrase support (coming in a follow-up).`,
    );
  }
  return decodeKey(raw);
}

/**
 * Decode a key string. Exposed for testing and for the future passphrase
 * resolver, which will scrypt-derive a Buffer and hand it back through here
 * to share the same length-check.
 */
export function decodeKey(raw: string): Buffer {
  const trimmed = raw.trim();
  // Hex first — strict 64 chars [0-9a-fA-F]
  if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {
    return Buffer.from(trimmed, 'hex');

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Generate and set a proper key: CLAUDE_FLOW_ENCRYPTION_KEY=$(openssl rand -hex 32) (or openssl rand -base64 32 for 44-char base64) alongside CLAUDE_FLOW_ENCRYPT_AT_REST
  2. If encryption was not intended, unset CLAUDE_FLOW_ENCRYPT_AT_REST — the vault only requires a key when the flag is set
  3. Export both variables together from one wrapper script or secret-manager entry so they can never diverge (flag without key)
  4. Verify the variable name is exactly CLAUDE_FLOW_ENCRYPTION_KEY (not CLAUDE_FLOW_ENCRYPTIONKEY or ENCRYPTION_KEY) with `env | grep CLAUDE_FLOW_ENCRYPT`

Example fix

# before — flag only, getKey() throws
export CLAUDE_FLOW_ENCRYPT_AT_REST=true
npx @claude-flow/cli@latest memory store --key k --value v

# after — flag + 32-byte hex key
export CLAUDE_FLOW_ENCRYPT_AT_REST=true
export CLAUDE_FLOW_ENCRYPTION_KEY="$(openssl rand -hex 32)"
npx @claude-flow/cli@latest memory store --key k --value v
Defensive patterns

Strategy: validation

Validate before calling

// Fail with your own contextual message before any vault-touching call
const flag = process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;
const key = process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
if (flag && !key) {
  throw new Error(
    'encrypt-at-rest enabled but CLAUDE_FLOW_ENCRYPTION_KEY is missing. ' +
    'Generate one: openssl rand -hex 32'
  );
}

Prevention

When it happens

Trigger: Setting CLAUDE_FLOW_ENCRYPT_AT_REST=true (or any value) in a shell, .env file, docker-compose environment block, or CI variables without also setting CLAUDE_FLOW_ENCRYPTION_KEY, then invoking any CLI/MCP path that initializes the vault (e.g. storing secrets or encrypted memory). Also triggered when the key variable name is misspelled (CLAUDE_FLOW_ENCRYPTION_KEY vs ENCRYPTION_KEY) or when a secret-manager injection step silently fails so only the flag reaches the process.

Common situations: CI pipelines that copy the enable flag from docs but never provision the key; Docker/Kubernetes env blocks where the flag is hardcoded but the key comes from a secret that was not mounted; teams enabling encrypt-at-rest after reading an ADR that mentions only the flag; wrapper scripts that export the flag globally in ~/.bashrc.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/2e0a65dac180346a. Report an issue: GitHub.