JuliusBrussee/caveman · error · Error
CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle
Error message
CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle
What it means
When CAVEMAN_WRAP_PROVIDER=bedrock selects the Bedrock transport for Claude wrap, CAVEMAN_BEDROCK_ENDPOINT chooses the lane: 'runtime' (Claude Code native Bedrock, the default when unset/empty) or 'mantle' (Claude Code's Mantle transport pointed at the gateway's /bedrock/anthropic adapter). The value is trimmed and lowercased before comparison, so only genuinely other strings throw; anything else is a typo or a stale value from an older Caveman that accepted different names.
Source
Thrown at packages/cli/src/index.ts:8171
if (!accessKey || !secretKey) {
throw new Error("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set together");
}
return [accessKey, secretKey, ...(sessionToken ? [sessionToken] : [])].join(":");
}
// applyClaudeBedrockWrap selects Claude Code's native Bedrock or opt-in Mantle
// transport only when the operator explicitly requests it. The default Claude
// profile remains Anthropic-wire. Runtime uses Claude Code's documented AWS
// credential chain; unlike Mantle, Claude Code exposes no supported Runtime
// authentication-bypass variable. In managed mode the validated BYOK value also
// rides through Claude's custom headers. Stored-only, server-injected Claude
// Code auth therefore uses Mantle's documented gateway mode.
function applyClaudeBedrockWrap(env: NodeJS.ProcessEnv, agent: AgentProfile, renderedGw: string, modeGw: string): boolean {
if (agent.id !== "claude" || process.env.CAVEMAN_WRAP_PROVIDER?.trim().toLowerCase() !== "bedrock") return false;
const endpoint = process.env.CAVEMAN_BEDROCK_ENDPOINT?.trim().toLowerCase() || "runtime";
if (endpoint !== "runtime" && endpoint !== "mantle") {
throw new Error("CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle");
}
// Remove the generic/profile Anthropic route and any stale Bedrock selection
// inherited from the shell. Exactly one Claude Code provider lane is active.
for (const key of WRAP_BASE_URL_ENV_VARS) delete env[key];
delete env.ANTHROPIC_AUTH_TOKEN;
delete env.CLAUDE_CODE_USE_BEDROCK;
// Remove stale values if a caller previously relied on this undocumented
// variable. We intentionally never set it.
delete env.CLAUDE_CODE_SKIP_BEDROCK_AUTH;
delete env.ANTHROPIC_BEDROCK_BASE_URL;
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";View on GitHub (pinned to 766dce6b13)
Solutions
- Use one of the two lane names: `export CAVEMAN_BEDROCK_ENDPOINT=runtime` or `export CAVEMAN_BEDROCK_ENDPOINT=mantle`
- If you wanted the default, just unset it: `unset CAVEMAN_BEDROCK_ENDPOINT` (empty/unset means runtime)
- Double-check you did not mean CAVEMAN_WRAP_PROVIDER (selects bedrock at all) or a gateway URL variable — URLs do not belong in CAVEMAN_BEDROCK_ENDPOINT
- Search your shell profile and CI env for the stray export and remove it
Example fix
# before: lane typo / URL in the lane variable export CAVEMAN_WRAP_PROVIDER=bedrock export CAVEMAN_BEDROCK_ENDPOINT=bedrock # or http://127.0.0.1:3777/bedrock caveman claude # >> CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle # after: valid lane (or unset for the default) export CAVEMAN_BEDROCK_ENDPOINT=runtime caveman claude
Defensive patterns
Strategy: validation
Validate before calling
const BEDROCK_ENDPOINTS = new Set(['runtime', 'mantle']);
const raw = process.env.CAVEMAN_BEDROCK_ENDPOINT;
const endpoint = raw?.trim().toLowerCase() || 'runtime';
if (process.env.CAVEMAN_WRAP_PROVIDER === 'bedrock' && !BEDROCK_ENDPOINTS.has(endpoint)) {
throw new Error(`CAVEMAN_BEDROCK_ENDPOINT must be one of ${[...BEDROCK_ENDPOINTS].join('|')} (or unset)`);
} Type guard
const isBedrockEndpointLane = (v: unknown): v is 'runtime' | 'mantle' => v === undefined || v === '' || v === 'runtime' || v === 'mantle';
Try / catch
catch (err) { if (err.message.includes('must be runtime or mantle')) { /* unset the variable (default runtime) or set it to a lane name — URLs and synonyms are rejected */ } else throw err; } Prevention
- Keep a project allowlist of caveman env vars and validate them at script start
- Remember the domain: lane names only — runtime (default) or mantle; URLs belong in gateway config
- Grep shell rc files and CI env blocks after upgrading caveman for variables whose vocab changed
When it happens
Trigger: `caveman wrap claude` / `caveman claude` with CAVEMAN_WRAP_PROVIDER=bedrock and CAVEMAN_BEDROCK_ENDPOINT set to a non-empty string other than runtime/mantle — e.g. 'bedrock', 'gateway', 'native', 'local', or a URL like http://127.0.0.1:3777/bedrock.
Common situations: Copying an endpoint URL into the wrong variable; values left over from an older release or an internal build that spelled the lane differently; scripts parameterizing the endpoint name with a typo.
Related errors
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set toge
- managed Bedrock wrap requires a valid CAVE_API_KEY
- ${name} must not contain a newline
- caveman build: set CAVE_MODEL when zero or multiple provider
- caveman-code: model must use provider/model format
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/dfeb968733e56d7b.
Report an issue: GitHub.