JuliusBrussee/caveman · error

bedrock: request signing failed

Error message

bedrock: request signing failed

What it means

The awssig.Signer.Sign call failed while computing the SigV4 signature. The proxy deliberately returns a generic message instead of the raw signer error, because signing operates on secret material; the raw cause is only that Sign returned non-nil err.

Solutions

  1. Verify the AWS credential value is the full accessKeyId:secretAccessKey[:sessionToken] form with no missing parts
  2. Confirm the region and service used by the signer (signingRegion / runtimeService vs mantleService) are correct for the target Bedrock endpoint
  3. Add temporary debug logging of the signer error (never the credentials) to identify the exact Sign failure, then fix the credential or request inputs

Example fix

// before
key := "AKIA..." // secret only, no secretAccessKey
// after
key := "AKIA...:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
Defensive patterns

Strategy: try-catch

Validate before calling

if cred.AccessKeyID == "" || cred.SecretAccessKey == "" { return errors.New("incomplete AWS credentials for SigV4") }

Try / catch

if err := sanitizeAndMap(); err != nil {
	if strings.Contains(err.Error(), "request signing failed") {
		log.Printf("SigV4 signing failed; check AWS keys/region (details not exposed for safety): %v", cause)
	}
}

Prevention

When it happens

Trigger: signer.Sign(toSign, creds, payloadHash, time.Now()) returns an error inside SanitizeAndMapHeaders — e.g. the credentials struct lacks an access key ID or secret, or the signer rejects the request form it was handed.

Common situations: AWS access keys parsed from x-cave-upstream-key are incomplete or contain characters the signer rejects; a session token of wrong form; signing invoked on a request whose required signed headers were stripped by earlier middleware.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/621c30eac78ddcb7. Report an issue: GitHub.

Appendix: source

Thrown at proxy/providers/bedrock/signing.go:122

	toSign, err := http.NewRequestWithContext(ctx, req.Method, upstream.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("bedrock: could not build signing request: %w", err)
	}
	toSign.Header = out.Clone()

	signingService := runtimeService
	if mantle {
		signingService = mantleService
	}
	payloadHash, err := requestPayloadHash(ctx, req)
	if err != nil {
		return nil, err
	}
	signer := awssig.Signer{Region: signingRegion(req, upstream), Service: signingService}
	if err := signer.Sign(toSign, creds, payloadHash, time.Now()); err != nil {
		// The error from Sign never contains the secret (see awssig docs); still,
		// return a generic mapping error rather than the raw text.
		return nil, fmt.Errorf("bedrock: request signing failed")
	}

	for _, name := range []string{"Authorization", "Host", "X-Amz-Date", "X-Amz-Content-Sha256", "X-Amz-Security-Token"} {
		if v := toSign.Header.Get(name); v != "" {
			out.Set(name, v)
		}
	}
	return out, nil
}

// matchesInboundSigningIdentity prevents a configured key or region from
// silently replacing the principal/region selected by the SDK. This checks the
// caller's scope claim, not the validity of its old signature: the standalone
// loopback proxy is not an IAM authenticator. The signer below creates a new
// signature covering the actual upstream authority, headers, and body.
func matchesInboundSigningIdentity(req *http.Request, upstream *url.URL, creds awssig.Credentials) bool {
	if len(req.Header.Values("Authorization")) != 1 {
		return false

View on GitHub (pinned to 3ee70a1026)