ruvnet/ruflo · error · Error
${ENV_ENABLE_FLAG} is set but ${ENV_KEY_VAR} is not. Provide
Error message
${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). What it means
Thrown by getKey() when CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY is not. Encrypt-at-rest is opt-in; enabling it without supplying a key would be unsafe, so the vault refuses rather than generating an ephemeral key that would make existing ciphertext undecryptable after restart. ADR-096 tracks future keychain/passphrase support.
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 6b01dc5a68)
Solutions
- Generate a 32-byte key and set CLAUDE_FLOW_ENCRYPTION_KEY to its 64-char hex: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`.
- Store the key in your secrets manager / .env (NEVER commit it) and ensure it is present in every process that reads or writes ciphertext.
- Until you have a key, unset CLAUDE_FLOW_ENCRYPT_AT_REST to disable the feature.
Example fix
# before
CLAUDE_FLOW_ENCRYPT_AT_REST=1
# CLAUDE_FLOW_ENCRYPTION_KEY missing
# after
CLAUDE_FLOW_ENCRYPT_AT_REST=1
CLAUDE_FLOW_ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") Defensive patterns
Strategy: validation
Validate before calling
function ensureVaultConfigured(): void {
const enabled = process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;
const key = process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
if (enabled && !key) {
throw new Error('CLAUDE_FLOW_ENCRYPT_AT_REST is set but CLAUDE_FLOW_ENCRYPTION_KEY is not');
}
}
ensureVaultConfigured(); Type guard
const vaultConfigured = (): boolean => !process.env.CLAUDE_FLOW_ENCRYPT_AT_REST || Boolean(process.env.CLAUDE_FLOW_ENCRYPTION_KEY);
Try / catch
try {
getKey();
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.includes('CLAUDE_FLOW_ENCRYPTION_KEY is not')) {
console.error('Generate a key: node -e "console.log(crypto.randomBytes(32).toString(\'hex\'))"');
process.exit(2);
}
throw e;
} Prevention
- Set both env vars together via your secrets manager.
- Generate the key once with crypto.randomBytes(32).toString('hex').
- Add a startup self-check that fails fast if the pair is inconsistent.
When it happens
Trigger: Setting CLAUDE_FLOW_ENCRYPT_AT_REST=1 (or any truthy value) in the environment without also setting CLAUDE_FLOW_ENCRYPTION_KEY, or setting it in one process/shell but not the one performing encryption.
Common situations: Configuring encrypt-at-rest in a .env file but forgetting the key line, deploying with a secrets manager that failed to inject the key, or enabling the flag locally to test without yet generating a key.
Related errors
- Invalid ${ENV_KEY_VAR}: expected 32-byte key as 64-char hex
- OPENAI_BASE_URL not set
- Router multimodal is enabled but LLM_ROUTER_MULTIMODAL_MODEL
- decryptBuffer: blob too short (${blob.length}B; need >= ${MI
- decryptBuffer: bad magic — blob is not Ruflo-encrypted (RFE1
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/6d8692f72e9bdbf2.
Report an issue: GitHub.