{"record":{"id":"5f8e492218a1e648","repo":"JuliusBrussee/caveman","slug":"bedrock-exact-request-payload-hash-is-unavailable","errorCode":null,"errorMessage":"bedrock: exact request payload hash is unavailable","messagePattern":"bedrock: exact request payload hash is unavailable","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/bedrock/signing.go","lineNumber":181,"sourceCode":"\t\tscope[2] == signingRegion(req, upstream) && scope[3] == service && scope[4] == \"aws4_request\" &&\n\t\treq.Header.Get(\"X-Amz-Security-Token\") == creds.SessionToken &&\n\t\t(!strings.HasPrefix(creds.AccessKeyID, \"ASIA\") || creds.SessionToken != \"\")\n}\n\n// requestPayloadHash returns the hash of the exact post-transform wire body.\n// Gateways install it in context after transforming. Direct adapter callers and\n// verification probes can fall back to net/http's replayable GetBody contract.\n// A non-replayable body without a bound hash fails closed instead of emitting a\n// signature over bytes that may differ from the request on the wire.\nfunc requestPayloadHash(ctx context.Context, req *http.Request) (string, error) {\n\tif hash, ok := providers.RequestPayloadHash(ctx); ok {\n\t\treturn hash, nil\n\t}\n\tif req.Body == nil {\n\t\treturn awssig.HashPayload(nil), nil\n\t}\n\tif req.GetBody == nil {\n\t\treturn \"\", fmt.Errorf(\"bedrock: exact request payload hash is unavailable\")\n\t}\n\tbody, err := req.GetBody()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"bedrock: exact request payload hash is unavailable\")\n\t}\n\tdefer body.Close()\n\traw, err := io.ReadAll(body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"bedrock: exact request payload hash is unavailable\")\n\t}\n\treturn awssig.HashPayload(raw), nil\n}\n\nfunc credentialAuthKind(credential providers.Credential) (string, error) {\n\tkind := strings.ToLower(strings.TrimSpace(credential.AuthKind))\n\tswitch kind {\n\tcase \"bedrock_api_key\", \"aws_access_keys\":\n\t\treturn kind, nil","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/bedrock/signing.go#L163-L199","documentation":"SigV4 must sign the SHA-256 hash of the exact request body. If the request has a body but no GetBody function, the proxy cannot re-read the bytes to hash them, so it fails closed rather than signing a wrong payload hash (which AWS would reject with a signature mismatch).","triggerScenarios":"requestPayloadHash is called on a request where req.Body != nil and req.GetBody == nil — typically a request body built from a stream/reader rather than bytes.Buffer/bytes.Reader/string, so http.NewRequestWithContext could not snapshot it.","commonSituations":"Constructing the proxied request with an io.Reader body (e.g. piping from the client connection) instead of a concrete byte slice; wrapping the body in a custom ReadCloser; earlier middleware replacing the body with a streaming reader.","solutions":["Build the request body from []byte or bytes.Buffer/strings.Reader so http.NewRequestWithContext automatically populates GetBody","If the body is already a stream, buffer it once (io.ReadAll) and reset req.Body with http.NoBody-free bytes.NewReader(raw), which also sets GetBody","Ensure no middleware replaces req.Body after construction without also setting req.GetBody"],"exampleFix":"// before\nreq, _ := http.NewRequestWithContext(ctx, \"POST\", url, io.Reader(clientStream))\n// after\nraw, _ := io.ReadAll(clientStream)\nreq, _ := http.NewRequestWithContext(ctx, \"POST\", url, bytes.NewReader(raw)) // GetBody set automatically","handlingStrategy":"validation","validationCode":"if req.Body != nil && req.GetBody == nil {\n\treturn errors.New(\"request body must be replayable (bytes.Reader/bytes.Buffer) for SigV4 signing\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build proxied bodies from []byte/bytes.Reader so GetBody is set","Never replace req.Body in middleware without also setting req.GetBody","Materialize streamed bodies once, early in the request pipeline"],"tags":["bedrock","sigv4","request-body"],"backgroundTag":"unsupported-operation","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"}