{"record":{"id":"e084268d21ff8b86","repo":"JuliusBrussee/caveman","slug":"aws-access-key-id-and-aws-secret-access-key-must-b","errorCode":null,"errorMessage":"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set together","messagePattern":"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set together","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":9259,"sourceCode":"  return kept.join(\"\\n\");\n}\n\nfunction bedrockCredentialEnvValue(env: NodeJS.ProcessEnv, name: string): string | undefined {\n  const raw = env[name];\n  if (typeof raw !== \"string\" || !raw.trim()) return undefined;\n  if (/[\\r\\n]/.test(raw)) throw new Error(`${name} must not contain a newline`);\n  return raw.trim();\n}\n\nfunction bedrockUpstreamCredentialFromEnv(env: NodeJS.ProcessEnv): string | undefined {\n  const bearer = bedrockCredentialEnvValue(env, \"AWS_BEARER_TOKEN_BEDROCK\");\n  if (bearer) return bearer;\n  const accessKey = bedrockCredentialEnvValue(env, \"AWS_ACCESS_KEY_ID\");\n  const secretKey = bedrockCredentialEnvValue(env, \"AWS_SECRET_ACCESS_KEY\");\n  const sessionToken = bedrockCredentialEnvValue(env, \"AWS_SESSION_TOKEN\");\n  if (!accessKey && !secretKey && !sessionToken) return undefined;\n  if (!accessKey || !secretKey) {\n    throw new Error(\"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set together\");\n  }\n  return [accessKey, secretKey, ...(sessionToken ? [sessionToken] : [])].join(\":\");\n}\n\n// applyClaudeBedrockWrap selects Claude Code's native Bedrock or opt-in Mantle\n// transport only when the operator explicitly requests it. The default Claude\n// profile remains Anthropic-wire. Runtime uses Claude Code's documented AWS\n// credential chain; unlike Mantle, Claude Code exposes no supported Runtime\n// authentication-bypass variable. In managed mode the validated BYOK value also\n// rides through Claude's custom headers. Stored-only, server-injected Claude\n// Code auth therefore uses Mantle's documented gateway mode.\nfunction applyClaudeBedrockWrap(env: NodeJS.ProcessEnv, agent: AgentProfile, renderedGw: string, modeGw: string): boolean {\n  if (agent.id !== \"claude\" || process.env.CAVEMAN_WRAP_PROVIDER?.trim().toLowerCase() !== \"bedrock\") return false;\n\n  const endpoint = process.env.CAVEMAN_BEDROCK_ENDPOINT?.trim().toLowerCase() || \"runtime\";\n  if (endpoint !== \"runtime\" && endpoint !== \"mantle\") {\n    throw new Error(\"CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle\");\n  }","sourceCodeStart":9241,"sourceCodeEnd":9277,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L9241-L9277","documentation":"When no Bedrock bearer token is present, the wrapper falls back to static AWS keys. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are only meaningful together; if exactly one of them (or only a session token) is set, the credential triple cannot be constructed, so it throws. AWS_SESSION_TOKEN alone is also insufficient.","triggerScenarios":"CAVEMAN_WRAP_PROVIDER=bedrock with AWS_SESSION_TOKEN set but neither AWS_ACCESS_KEY_ID nor AWS_SECRET_ACCESS_KEY; or only AWS_ACCESS_KEY_ID exported (or only the secret key) in the shell/CI environment.","commonSituations":"Partially populated CI/CD secrets; exporting keys across two shell sessions; assuming a session token alone is usable; a .env file where one of the pair was commented out.","solutions":["Export both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY together (plus AWS_SESSION_TOKEN if using temporary credentials).","Alternatively set AWS_BEARER_TOKEN_BEDROCK to skip the key-based path entirely.","Audit your CI secret list / .env to confirm the key pair was not truncated or partially defined.","If you have no Bedrock credentials, unset all three variables so the function cleanly returns undefined."],"exampleFix":"// before (shell)\nexport AWS_SESSION_TOKEN=\"FwoGZX...\"\n// after\nexport AWS_ACCESS_KEY_ID=\"AKIA...\"\nexport AWS_SECRET_ACCESS_KEY=\"wJalr...\"\nexport AWS_SESSION_TOKEN=\"FwoGZX...\"","handlingStrategy":"validation","validationCode":"const has = (k: string) => typeof process.env[k] === \"string\" && process.env[k]!.trim() !== \"\";\nif (!has(\"AWS_BEARER_TOKEN_BEDROCK\")) {\n  const a = has(\"AWS_ACCESS_KEY_ID\"), s = has(\"AWS_SECRET_ACCESS_KEY\");\n  if (a !== s) throw new Error(\"Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY together (or neither, using a bearer token)\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  applyBedrockWrap(env, agent);\n} catch (e) {\n  if (String((e as Error).message).includes(\"must be set together\")) {\n    // surface which of the pair is missing and abort with instructions\n  }\n}","preventionTips":["Export the AWS key pair (and session token) as a single atomic set in scripts/CI.","Prefer AWS_BEARER_TOKEN_BEDROCK when using token-based Bedrock auth.","Keep AWS credentials in one .env block; never comment out half a pair.","Run an env preflight check before enabling CAVEMAN_WRAP_PROVIDER=bedrock."],"tags":["env","aws","bedrock","credentials","partial-config"],"backgroundTag":"missing-credentials","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"}