JuliusBrussee/caveman · error · Error
managed Bedrock wrap requires a valid CAVE_API_KEY
Error message
managed Bedrock wrap requires a valid CAVE_API_KEY
What it means
In managed gateway mode (managed CAVE gateway URL), Bedrock-wrapped Claude must authenticate to the gateway itself, so Caveman requires CAVE_API_KEY: it must be present in the environment (firstEnvSecret), non-empty, and free of \r/\n — the key is delivered via the x-cave-api-key line of ANTHROPIC_CUSTOM_HEADERS, where a newline would enable header injection. Local-mode wraps do not hit this check because the local gateway does not authenticate.
Source
Thrown at packages/cli/src/index.ts:8201
delete env.CLAUDE_CODE_USE_MANTLE;
delete env.CLAUDE_CODE_SKIP_MANTLE_AUTH;
delete env.ANTHROPIC_BEDROCK_MANTLE_BASE_URL;
const bedrockBase = appendUrlPath(renderedGw, "/bedrock");
if (endpoint === "mantle") {
env.CLAUDE_CODE_USE_MANTLE = "1";
env.CLAUDE_CODE_SKIP_MANTLE_AUTH = "1";
// Claude Code appends /v1/messages verbatim to this override. Caveman's
// explicit Mantle adapter route is /bedrock/anthropic/v1/messages.
env.ANTHROPIC_BEDROCK_MANTLE_BASE_URL = appendUrlPath(bedrockBase, "/anthropic");
} else {
env.CLAUDE_CODE_USE_BEDROCK = "1";
env.ANTHROPIC_BEDROCK_BASE_URL = bedrockBase;
}
if (wrapMode(modeGw) === "managed") {
const caveAPIKey = firstEnvSecret(env, ["CAVE_API_KEY"]);
if (!caveAPIKey || /[\r\n]/.test(caveAPIKey)) {
throw new Error("managed Bedrock wrap requires a valid CAVE_API_KEY");
}
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeader(
env.ANTHROPIC_CUSTOM_HEADERS,
"x-cave-api-key",
caveAPIKey,
);
const upstreamKey = bedrockUpstreamCredentialFromEnv(env);
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeader(
env.ANTHROPIC_CUSTOM_HEADERS,
"x-cave-upstream-key",
upstreamKey,
);
}
return true;
}
// buildWrapEnv computes the child environment for a wrapped agent. It starts from
// the generic provider base-URL union (the fail-open fallback — harmless for anView on GitHub (pinned to 766dce6b13)
Solutions
- Export a single-line key: `export CAVE_API_KEY=$(printf '%s' "$CAVE_API_KEY" | tr -d '\r\n')` using your real key, then rerun
- If wrapping locally, make sure the gateway URL actually resolves to local mode (CAVE_GATEWAY_URL / caveman start) — local mode needs no CAVE_API_KEY
- In CI, reference the secret on one line (`CAVE_API_KEY: ${{ secrets.CAVE_API_KEY }}`), never via a '|' block scalar
- Verify with `node -e 'console.log(process.env.CAVE_API_KEY?.length)'` that the variable is visible and non-empty in the exact shell that runs caveman
Example fix
# before: managed bedrock wrap without a usable key export CAVEMAN_WRAP_PROVIDER=bedrock caveman claude # >> managed Bedrock wrap requires a valid CAVE_API_KEY # after: single-line key exported, wrap proceeds export CAVE_API_KEY=cave_live_9f8e7d6c... caveman claude
Defensive patterns
Strategy: validation
Validate before calling
const key = process.env.CAVE_API_KEY;
const usable = typeof key === 'string' && key.length > 0 && !/[\r\n]/.test(key);
if (process.env.CAVEMAN_WRAP_PROVIDER === 'bedrock' && isManagedGatewayUrl(process.env.CAVE_GATEWAY_URL) && !usable) {
throw new Error('export a single-line CAVE_API_KEY before managed bedrock wrap');
} Type guard
const isUsableApiKey = (v: unknown): v is string => typeof v === 'string' && v.length > 0 && !/[\r\n]/.test(v);
Try / catch
catch (err) { if (err.message.includes('requires a valid CAVE_API_KEY')) { /* export a clean single-line key (strip CR/LF), or switch the wrap to local mode; retry once */ } else throw err; } Prevention
- Export CAVE_API_KEY with `tr -d '\r\n'` normalization in CI loaders
- Local-mode wraps need no key — verify CAVE_GATEWAY_URL really points at the managed gateway before assuming key trouble
- Keep the key in the environment of whatever shell/runner invokes caveman, not just your login shell
When it happens
Trigger: `caveman claude` / `caveman wrap claude` with CAVEMAN_WRAP_PROVIDER=bedrock and a managed gateway URL, when CAVE_API_KEY is unset, empty, whitespace-only, or contains a line break (e.g. copied with a trailing newline from a password manager or a CI block scalar).
Common situations: Forgetting to export the key in a fresh shell or CI job; the key living only in ~/.caveman-cloud credentials while the wrap expects it in env; a secrets manager appending \n; switching a working local-mode setup to the managed gateway without adding the key.
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
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set toge
- CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle
- caveman-code: no supported provider credential found; set AN
- ${name} must not contain a newline
- caveman build: set CAVE_MODEL when zero or multiple provider
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/437817aa5530b524.
Report an issue: GitHub.