JuliusBrussee/caveman · error

ErrSigV4Configuration

ErrSigV4Configuration

Error message

AWS SigV4 requests require matching AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN (when used), and region in the proxy process; configure them or use a Bedrock bearer API key

What it means

Bedrock SigV4 signing requires a complete, consistent IAM credential set (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, optional AWS_SESSION_TOKEN) plus a region inside the proxy process. ErrSigV4Configuration is a deliberately safe sentinel: it carries no credential material and is mapped by the gateway to HTTP 400 with code 'cave_bedrock_sigv4_configuration'.

Solutions

  1. Export AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN if applicable) plus AWS_REGION in the proxy process environment
  2. Attach an IAM role to the compute environment (instance profile, ECS task role, IRSA) so the credential chain resolves
  3. Use a Bedrock bearer API key instead of SigV4 for this provider path
  4. Verify the client's SigV4 credentials match the proxy-side credentials — mismatched pairs are rejected

Example fix

// before
export AWS_ACCESS_KEY_ID=AKIA...
// after (complete set)
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
export AWS_REGION=us-east-1
Defensive patterns

Strategy: validation

Validate before calling

func hasSigV4Env() bool {
	id, sec := os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY")
	return id != "" && sec != "" && os.Getenv("AWS_REGION") != ""
}

Try / catch

if err != nil {
	if errors.Is(err, bedrock.ErrSigV4Configuration) {
		// 400: surface setup guidance to the operator
		httpx.Error(w, r, http.StatusBadRequest, "cave_bedrock_sigv4_configuration", err.Error())
		return
	}
}

Prevention

When it happens

Trigger: A Bedrock request arrives carrying SigV4-signed headers (or the adapter opts into SigV4) while the proxy's awscreds chain finds no usable IAM credentials or region — e.g. only AWS_ACCESS_KEY_ID set, or a Bedrock bearer key absent and env vars missing.

Common situations: Running the proxy locally with only a Bedrock API key while the client sends SigV4 headers; deploying to an environment without instance role/IRSA; setting a session token in one place but not the other; forgetting AWS_REGION in the proxy env.

Related errors


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

Appendix: source

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

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/JuliusBrussee/caveman/proxy/providers"
	"github.com/JuliusBrussee/caveman/shared/platform/awssig"
)

// ErrSigV4Configuration is safe to show to the caller: it contains no credential
// material. An inbound signature is not a reusable credential after the proxy
// changes the request authority/path or body.
var ErrSigV4Configuration = errors.New("AWS SigV4 requests require matching AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN (when used), and region in the proxy process; configure them or use a Bedrock bearer API key")

// SanitizeAndMapHeaders builds the upstream header set for Bedrock Runtime or
// Mantle. Bedrock API keys use a bearer on Runtime and x-api-key on Mantle. IAM
// access keys are SigV4-signed with the endpoint's distinct service name.
//
// IAM credentials retain the legacy "accessKeyId:secretAccessKey[:sessionToken]"
// encoding at the adapter boundary. The secret is consumed only to derive the
// signature and never copied into a forwarded header, log, error, or telemetry.
func (a Adapter) SanitizeAndMapHeaders(ctx context.Context, req *http.Request, credential providers.Credential, upstream *url.URL) (http.Header, error) {
	out := http.Header{}
	copyIfPresent(out, req.Header, "content-type")
	copyIfPresent(out, req.Header, "content-encoding")
	copyIfPresent(out, req.Header, "accept")
	copyIfPresent(out, req.Header, "accept-encoding")
	mantle := endpointKindForPath(req.URL.Path) == endpointMantle
	if mantle {
		copyIfPresent(out, req.Header, "anthropic-version")
		copyIfPresent(out, req.Header, "anthropic-beta")

View on GitHub (pinned to 3ee70a1026)