JuliusBrussee/caveman · error
bedrock: exact request payload hash is unavailable
Error message
bedrock: exact request payload hash is unavailable
What it means
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).
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
Example fix
// before req, _ := http.NewRequestWithContext(ctx, "POST", url, io.Reader(clientStream)) // after raw, _ := io.ReadAll(clientStream) req, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(raw)) // GetBody set automatically
Defensive patterns
Strategy: validation
Validate before calling
if req.Body != nil && req.GetBody == nil {
return errors.New("request body must be replayable (bytes.Reader/bytes.Buffer) for SigV4 signing")
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- bedrock: could not build signing request
- bedrock: missing AWS credentials in x-cave-upstream-key
- bedrock: request signing failed
- bedrock requested region
- ErrSigV4Configuration
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5f8e492218a1e648.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/providers/bedrock/signing.go:181
scope[2] == signingRegion(req, upstream) && scope[3] == service && scope[4] == "aws4_request" &&
req.Header.Get("X-Amz-Security-Token") == creds.SessionToken &&
(!strings.HasPrefix(creds.AccessKeyID, "ASIA") || creds.SessionToken != "")
}
// requestPayloadHash returns the hash of the exact post-transform wire body.
// Gateways install it in context after transforming. Direct adapter callers and
// verification probes can fall back to net/http's replayable GetBody contract.
// A non-replayable body without a bound hash fails closed instead of emitting a
// signature over bytes that may differ from the request on the wire.
func requestPayloadHash(ctx context.Context, req *http.Request) (string, error) {
if hash, ok := providers.RequestPayloadHash(ctx); ok {
return hash, nil
}
if req.Body == nil {
return awssig.HashPayload(nil), nil
}
if req.GetBody == nil {
return "", fmt.Errorf("bedrock: exact request payload hash is unavailable")
}
body, err := req.GetBody()
if err != nil {
return "", fmt.Errorf("bedrock: exact request payload hash is unavailable")
}
defer body.Close()
raw, err := io.ReadAll(body)
if err != nil {
return "", fmt.Errorf("bedrock: exact request payload hash is unavailable")
}
return awssig.HashPayload(raw), nil
}
func credentialAuthKind(credential providers.Credential) (string, error) {
kind := strings.ToLower(strings.TrimSpace(credential.AuthKind))
switch kind {
case "bedrock_api_key", "aws_access_keys":
return kind, nilView on GitHub (pinned to 3ee70a1026)