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
- 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
- If encryption was not intended, unset CLAUDE_FLOW_ENCRYPT_AT_REST — the vault only requires a key when the flag is set
- Export both variables together from one wrapper script or secret-manager entry so they can never diverge (flag without key)
- 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
- Export CLAUDE_FLOW_ENCRYPT_AT_REST and CLAUDE_FLOW_ENCRYPTION_KEY together from one wrapper script or secret-manager entry so they never diverge
- Add a startup env assertion in CI/boot scripts (fail before work starts, not mid-run)
- Document key generation (openssl rand -hex 32) next to every mention of the flag
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
- Invalid CLAUDE_FLOW_ENCRYPTION_KEY: expected 32-byte key as
- Pool ${this.id} at maximum capacity (${this.config.maxWorker
- Invalid completion type
- MCP server "${server.name}" returned HTTP ${httpStatus}: ${h
- No endpoints configured. This build requires OpenAI-compatib
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/2e0a65dac180346a.
Report an issue: GitHub.