{"record":{"id":"437817aa5530b524","repo":"JuliusBrussee/caveman","slug":"managed-bedrock-wrap-requires-a-valid-cave-api-key","errorCode":null,"errorMessage":"managed Bedrock wrap requires a valid CAVE_API_KEY","messagePattern":"managed Bedrock wrap requires a valid CAVE_API_KEY","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":9306,"sourceCode":"  delete env.CLAUDE_CODE_USE_MANTLE;\n  delete env.CLAUDE_CODE_SKIP_MANTLE_AUTH;\n  delete env.ANTHROPIC_BEDROCK_MANTLE_BASE_URL;\n\n  const bedrockBase = appendUrlPath(renderedGw, \"/bedrock\");\n  if (endpoint === \"mantle\") {\n    env.CLAUDE_CODE_USE_MANTLE = \"1\";\n    env.CLAUDE_CODE_SKIP_MANTLE_AUTH = \"1\";\n    // Claude Code appends /v1/messages verbatim to this override. Caveman's\n    // explicit Mantle adapter route is /bedrock/anthropic/v1/messages.\n    env.ANTHROPIC_BEDROCK_MANTLE_BASE_URL = appendUrlPath(bedrockBase, \"/anthropic\");\n  } else {\n    env.CLAUDE_CODE_USE_BEDROCK = \"1\";\n    env.ANTHROPIC_BEDROCK_BASE_URL = bedrockBase;\n  }\n  if (wrapMode(modeGw) === \"managed\") {\n    const caveAPIKey = firstEnvSecret(env, [\"CAVE_API_KEY\"]);\n    if (!caveAPIKey || /[\\r\\n]/.test(caveAPIKey)) {\n      throw new Error(\"managed Bedrock wrap requires a valid CAVE_API_KEY\");\n    }\n    env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeader(\n      env.ANTHROPIC_CUSTOM_HEADERS,\n      \"x-cave-api-key\",\n      caveAPIKey,\n    );\n    const upstreamKey = bedrockUpstreamCredentialFromEnv(env);\n    env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeader(\n      env.ANTHROPIC_CUSTOM_HEADERS,\n      \"x-cave-upstream-key\",\n      upstreamKey,\n    );\n  }\n  return true;\n}\n\n// buildWrapEnv computes the child environment for a wrapped agent. It starts from\n// the generic provider base-URL union (the fail-open fallback — harmless for an","sourceCodeStart":9288,"sourceCodeEnd":9324,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L9288-L9324","documentation":"When Caveman wraps the Claude agent for managed Bedrock routing, it must forward your identity to the Cave gateway via an `x-cave-api-key` header. Before injecting that header it reads CAVE_API_KEY from the process environment; if the variable is unset/empty, or if it contains a CR or LF character (which would allow HTTP header injection into ANTHROPIC_CUSTOM_HEADERS), the wrapper aborts the launch rather than produce a broken or unsafe request. This is a deliberate fail-fast: routing through managed Bedrock without a valid key cannot work.","triggerScenarios":"Calling the CLI launcher (the code path in packages/cli/src/index.ts around buildWrapEnv/applyClaudeBedrockWrap) with a Bedrock wrap mode (env.CLAUDE_CODE_USE_BEDROCK=\"1\" and ANTHROPIC_BEDROCK_BASE_URL set to the managed base URL) while (a) CAVE_API_KEY is not exported in the environment passed to firstEnvSecret, or (b) CAVE_API_KEY contains a carriage return or newline character.","commonSituations":"Running the managed Bedrock wrapper in CI or a fresh shell where CAVE_API_KEY was never exported; a .env loader that preserves the literal '\\n' escape in the key value; copy-pasting the key from a document with a trailing newline; wrapping multi-line secrets from a secret manager into the env var.","solutions":["Export a valid CAVE_API_KEY in the environment before launching: export CAVE_API_KEY=<key from your Caveman dashboard> (or add it to the env your wrapper actually reads).","Strip whitespace/newlines from the value: export CAVE_API_KEY=$(printf '%s' \"$CAVE_API_KEY\" | tr -d '\\r\\n'), or fix the .env file so the key is on one line without escaped \\n.","If you do not intend managed Bedrock routing, switch the wrap mode/gateway configuration so wrapMode(gw) !== \"managed\" (e.g. use direct Bedrock credentials instead of the managed wrap)."],"exampleFix":"// before (missing or malformed secret)\nCLAUDE_CODE_USE_BEDROCK=1 npx caveman agent claude   # throws: managed Bedrock wrap requires a valid CAVE_API_KEY\n// after\nexport CAVE_API_KEY=\"sk-cave-...\"   # single line, no CR/LF\nCLAUDE_CODE_USE_BEDROCK=1 npx caveman agent claude","handlingStrategy":"validation","validationCode":"const key = process.env.CAVE_API_KEY;\nif (!key || /[\\r\\n]/.test(key)) {\n  throw new Error(\"Refusing to launch managed Bedrock wrap: CAVE_API_KEY missing or contains newline\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  launchManagedBedrockWrap();\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"CAVE_API_KEY\")) {\n    console.error(\"Set CAVE_API_KEY (single line, no CR/LF) before using managed Bedrock wrap.\");\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Add CAVE_API_KEY to your shell profile or secret manager so it is present in every environment the wrapper runs in.","Never paste keys with trailing newlines into .env files; trim CR/LF when loading secrets programmatically.","In CI, assert the key exists (and is newline-free) as a pre-flight step before invoking the wrapper."],"tags":["bedrock","api-key","environment","header-injection","managed-routing"],"backgroundTag":"missing-api-key","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}