JuliusBrussee/caveman · error · Error
${name} must not contain a newline
Error message
${name} must not contain a newline What it means
In the managed Bedrock wrap path (CAVEMAN_WRAP_PROVIDER=bedrock with Claude), AWS credential env values are merged into Claude Code's ANTHROPIC_CUSTOM_HEADERS (x-cave-upstream-key). Because that variable is newline-separated 'Name: Value' lines, a credential containing \r or \n would allow header injection into requests, so bedrockCredentialEnvValue rejects any of AWS_BEARER_TOKEN_BEDROCK, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or AWS_SESSION_TOKEN whose value contains a newline. Empty/whitespace values are treated as unset, not errors.
Source
Thrown at packages/cli/src/index.ts:8142
// auth header, then append exactly one current value when one is supplied.
function mergeAnthropicCustomHeader(raw: string | undefined, name: string, value: string | undefined): string {
const target = name.toLowerCase();
const kept = (raw ?? "")
.split(/\r\n|\n|\r/)
.filter((line) => {
if (!line.trim()) return false;
const colon = line.indexOf(":");
const headerName = (colon < 0 ? line : line.slice(0, colon)).trim().toLowerCase();
return headerName !== target;
});
if (value !== undefined) kept.push(`${name}: ${value}`);
return kept.join("\n");
}
function bedrockCredentialEnvValue(env: NodeJS.ProcessEnv, name: string): string | undefined {
const raw = env[name];
if (typeof raw !== "string" || !raw.trim()) return undefined;
if (/[\r\n]/.test(raw)) throw new Error(`${name} must not contain a newline`);
return raw.trim();
}
function bedrockUpstreamCredentialFromEnv(env: NodeJS.ProcessEnv): string | undefined {
const bearer = bedrockCredentialEnvValue(env, "AWS_BEARER_TOKEN_BEDROCK");
if (bearer) return bearer;
const accessKey = bedrockCredentialEnvValue(env, "AWS_ACCESS_KEY_ID");
const secretKey = bedrockCredentialEnvValue(env, "AWS_SECRET_ACCESS_KEY");
const sessionToken = bedrockCredentialEnvValue(env, "AWS_SESSION_TOKEN");
if (!accessKey && !secretKey && !sessionToken) return undefined;
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 ClaudeView on GitHub (pinned to 766dce6b13)
Solutions
- Re-export the offending variable as a single line: `export AWS_ACCESS_KEY_ID=$(printf '%s' "$AWS_ACCESS_KEY_ID" | tr -d '\r\n')` and retry
- Fix the source: in CI YAML use a single-line secret reference (`AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}`) instead of a block scalar; in .env keep the value on one line
- Strip CRLF when loading from files: `export AWS_BEARER_TOKEN_BEDROCK=$(tr -d '\r\n' < token.txt)`
- If the multi-line value is legitimate (e.g. a PEM), it is not a valid header credential — use a single-line token or key pair instead
Example fix
# before: block scalar injects a newline (CI yaml)
env:
AWS_SECRET_ACCESS_KEY: |
abc123
# after: single-line secret reference
env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} Defensive patterns
Strategy: type-guard
Validate before calling
const isSingleLine = (v) => typeof v === 'string' && v.length > 0 && !/[\r\n]/.test(v);
for (const name of ['AWS_BEARER_TOKEN_BEDROCK', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN']) {
if (process.env[name] !== undefined && !isSingleLine(process.env[name])) {
throw new Error(`${name} contains a newline — fix it before wrap`);
}
} Type guard
const isSingleLineSecret = (v: unknown): v is string => typeof v === 'string' && v.length > 0 && !/[\r\n]/.test(v);
Try / catch
catch (err) { if (/must not contain a newline/.test(err.message)) { /* strip CR/LF from the named env var and retry once */ } else throw err; } Prevention
- Reference CI secrets inline (`VAR: ${{ secrets.X }}`), never as YAML block scalars
- Normalize on load: `export K=$(tr -d '\r\n' < value)` for anything pasted or stored in files
- Treat multi-line PEMs as a signal you grabbed the wrong credential format
When it happens
Trigger: Running `caveman wrap claude` (or `caveman claude`) with CAVEMAN_WRAP_PROVIDER=bedrock in managed mode when one of the AWS credential variables above holds a multi-line string — typically a YAML block scalar in CI, a .env file with embedded line breaks, or a pasted PEM-style token.
Common situations: GitHub Actions/GitLab CI 'env: |' block scalars accidentally folding a key across lines; a shell export with an unterminated quote swallowing a newline; secrets managers returning values with trailing CRLF on Windows.
Related errors
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set toge
- CAVEMAN_BEDROCK_ENDPOINT must be runtime or mantle
- managed Bedrock wrap requires a valid CAVE_API_KEY
- bedrock endpoint must be runtime or mantle
- bedrock endpoint must be runtime or mantle
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/af9c0b2c2a258227.
Report an issue: GitHub.