{"record":{"id":"9c0730683ba55b48","repo":"headroomlabs-ai/headroom","slug":"bedrock-sigv4-failed","errorCode":"bedrock_sigv4_failed","errorMessage":"sigv4 signing failed: {0}","messagePattern":"sigv4 signing failed: (.+?)","errorType":"http","errorClass":"SigV4Error::Sign","httpStatus":500,"severity":"error","filePath":"crates/headroom-proxy/src/bedrock/sigv4.rs","lineNumber":94,"sourceCode":"}\n\n/// Headers that the signer will write into the outbound request.\n/// The handler must add every entry to the upstream-bound HeaderMap\n/// before sending — Bedrock validates each header against the\n/// canonical request.\n#[derive(Debug, Clone)]\npub struct SignedHeaders {\n    pub entries: Vec<(String, String)>,\n    /// Lowercase hex SHA-256 of the body. Surfaced for tests + logs.\n    pub signature: String,\n}\n\n/// Errors surfaced by the signing path.\n#[derive(Debug, Error)]\npub enum SigV4Error {\n    /// `aws-sigv4` rejected the request (URL parse, malformed header,\n    /// etc).\n    #[error(\"sigv4 signing failed: {0}\")]\n    Sign(String),\n    /// The signing-params builder rejected the inputs (e.g. missing\n    /// region — should never happen because we validate at startup).\n    #[error(\"sigv4 builder error: {0}\")]\n    Builder(String),\n}\n\n/// Sign a Bedrock request and return the headers the handler must add\n/// to the outbound request.\n///\n/// # Cache safety\n///\n/// The body bytes passed in MUST be the bytes the proxy is about to\n/// send upstream. If the compressor mutated the body, those mutated\n/// bytes are what get signed — Bedrock will accept because the\n/// signature covers the wire payload, not the original.\n///\n/// # Errors","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/crates/headroom-proxy/src/bedrock/sigv4.rs#L76-L112","documentation":"The aws-sigv4 signing crate rejected the Bedrock request during signing — URL parse failure or malformed header are the canonical causes listed on the variant. Signing happens per-request in sigv4.rs before the outbound call, so this aborts the request before it ever reaches Bedrock.","triggerScenarios":"Sign() invoked with a URL the signer cannot parse (unusual scheme, invalid characters); a request header value containing bytes/characters aws-sigv4 refuses; an HTTP method or signing configuration the crate's HttpSignatureProblem wrapping rejects.","commonSituations":"A custom --bedrock-base-url / endpoint override with a malformed URL; injected headers (from client passthrough) containing newlines or non-ASCII that poison signing; version bump of aws-sigv4 tightening validation on previously-tolerated input.","solutions":["Inspect the {0} inner string — it embeds the aws-sigv4 error and names the offending component (URL vs header).","Validate/normalize the configured Bedrock endpoint URL at startup (scheme + host well-formed) so bad config fails fast, not per-request.","Sanitize or drop hop-by-hop and malformed client headers before signing rather than passing them through.","Pin or align the aws-sigv4 version with what this crate was tested against if an upgrade introduced stricter checks."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate the endpoint once at startup\nfn valid_upstream(url: &str) -> bool {\n    url::Url::parse(url).map(|u| matches!(u.scheme(), \"http\" | \"https\")).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match sign_request(&req, &creds, region).await {\n    Err(SigV4Error::Sign(e)) => {\n        tracing::error!(error = %e, \"signing rejected request\");\n        // fail the request — signing failures are not transient\n        return Err(ProxyError::InvalidHeader(e));\n    }\n    r => r,\n}","preventionTips":["Parse and validate the configured Bedrock endpoint URL at startup.","Sanitize client-supplied headers before they enter the signing set.","Pin aws-sigv4 to a tested version."],"tags":["bedrock","sigv4","auth","aws","rust"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}