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 Claude

View on GitHub (pinned to 766dce6b13)

Solutions

  1. 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
  2. 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
  3. Strip CRLF when loading from files: `export AWS_BEARER_TOKEN_BEDROCK=$(tr -d '\r\n' < token.txt)`
  4. 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

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


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/af9c0b2c2a258227. Report an issue: GitHub.