JuliusBrussee/caveman · error · Error
cave_claude_header_invalid
Error message
cave_claude_header_invalid
What it means
mergeClaudeHeader() builds raw HTTP header text that gets injected into the SDK's header configuration, so header names containing ':' or CR/LF, or values containing CR/LF, are rejected outright — they would allow header smuggling or request splitting. Any violation throws cave_claude_header_invalid before the request is made.
Source
Thrown at packages/agent/src/claude-runtime.ts:451
for (const [name, value] of Object.entries(headers)) {
raw = mergeClaudeHeader(raw, name, value);
}
return raw ?? "";
}
function stripCaveHeaders(raw: string | undefined): string | undefined {
const kept = (raw ?? "").split(/\r\n|\n|\r/).filter((line) => {
if (!line.trim()) return false;
const colon = line.indexOf(":");
const name = (colon < 0 ? line : line.slice(0, colon)).trim().toLowerCase();
return !name.startsWith("x-cave-");
});
return kept.length === 0 ? undefined : kept.join("\n");
}
function mergeClaudeHeader(raw: string | undefined, name: string, value: string): string {
if (/[:\r\n]/.test(name) || /[\r\n]/.test(value)) {
throw new Error("cave_claude_header_invalid");
}
const target = name.toLowerCase();
const kept = (raw ?? "").split(/\r\n|\n|\r/).filter((line) => {
if (!line.trim()) return false;
const colon = line.indexOf(":");
return (colon < 0 ? line : line.slice(0, colon)).trim().toLowerCase() !== target;
});
kept.push(`${name}: ${value}`);
return kept.join("\n");
}
function resolveClaudeModel(definition: AgentDefinition, rootDir: string): string {
const configured = typeof definition.model === "string"
? definition.model
: process.env.CAVE_MODEL ?? localModel(rootDir) ??
(process.env.ANTHROPIC_API_KEY ? "anthropic/claude-haiku-4-5" : undefined);
if (configured === undefined) throw new Error("cave_claude_model_required");
const separator = configured.indexOf("/");View on GitHub (pinned to 27d5a3981a)
Solutions
- Trim and single-line the value: value.replace(/[\r\n]+/g, "").trim() before passing it in.
- Fix the source: strip CRLF when loading config files, or quote env values so the shell doesn't embed newlines.
- Validate header names are token-shaped (^[A-Za-z0-9-]+$) at your config layer so a colon can never reach mergeClaudeHeader.
Example fix
// before: value read from a CRLF config file
const token = readLine("auth.txt"); // "abc123\r"
mergeClaudeHeader(raw, "authorization", token); // throws
// after: normalize before merging
const token = readLine("auth.txt").replace(/[\r\n]+/g, "").trim();
mergeClaudeHeader(raw, "authorization", token); Defensive patterns
Strategy: validation
Validate before calling
function assertHeaderSafe(name: string, value: string): void {
if (!/^[!#$%&'*+.^_|~0-9A-Za-z-]+$/.test(name) || /[\r\n]/.test(value)) {
throw new Error(`unsafe header ${name}`);
}
}
// run before composing options that reach the Claude harness
assertHeaderSafe("x-cave-trace", traceValue.replace(/[\r\n]+/g, "").trim()); Type guard
function isSafeHeaderValue(value: string): boolean {
return !/[\r\n]/.test(value);
} Try / catch
try {
merged = mergeClaudeHeader(merged, name, value);
} catch (error) {
if (error instanceof Error && error.message === "cave_claude_header_invalid") {
// source data is malformed — sanitize or reject the config, never retry as-is
throw new Error(`header '${name}' rejected: sanitize CR/LF and ':' before retry`);
}
throw error;
} Prevention
- Strip CR/LF and trim every header value sourced from files, env, or user input before it reaches the harness options.
- Restrict header names to a token regex at your config boundary.
- On Windows, normalize CRLF when reading config files that feed header values.
When it happens
Trigger: Passing a header name via the Claude harness options that includes a colon (e.g. "X-Cave-Trace: extra") or any \r/\n; or a header value with embedded newlines (multi-line tokens, pasted certs, values read from a file with trailing CRLF that wasn't trimmed).
Common situations: Reading header values from .env or config files on Windows (CRLF line endings leak \r into values); copy-pasting multi-line API keys or bearer tokens; programmatically composing header names from user input without sanitization.
Related errors
- cave_eve_terminal_${result.status}
- cave_harness_upstream_version_mismatch
- cave_mastra_max_steps_invalid
- cave_mastra_terminal_failure
- cave_harness_incomplete_evidence
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/c7a8d91e5ca3fa7a.
Report an issue: GitHub.