JuliusBrussee/caveman · critical

cache-replay: Bedrock bearer token or AWS access credentials

Error message

cache-replay: Bedrock bearer token or AWS access credentials unavailable

What it means

Returned by validateProviderCredentials for bedrock traces: neither AWS_BEARER_TOKEN_BEDROCK is set nor the classic AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY pair is complete (both must be non-empty). Bedrock accepts either a bearer token or long-lived access keys, and the check fails only when neither path is fully satisfied.

Source

Thrown at cacheengine/cmd/cache-replay/main.go:389

		providers[record.Provider] = true
	}
	for provider := range providers {
		switch provider {
		case "openai":
			if os.Getenv("OPENAI_API_KEY") == "" {
				return errors.New("cache-replay: OPENAI_API_KEY unavailable")
			}
		case "anthropic":
			if os.Getenv("ANTHROPIC_API_KEY") == "" {
				return errors.New("cache-replay: ANTHROPIC_API_KEY unavailable")
			}
		case "gemini":
			if os.Getenv("GEMINI_API_KEY") == "" {
				return errors.New("cache-replay: GEMINI_API_KEY unavailable")
			}
		case "bedrock":
			if os.Getenv("AWS_BEARER_TOKEN_BEDROCK") == "" && (os.Getenv("AWS_ACCESS_KEY_ID") == "" || os.Getenv("AWS_SECRET_ACCESS_KEY") == "") {
				return errors.New("cache-replay: Bedrock bearer token or AWS access credentials unavailable")
			}
		default:
			return fmt.Errorf("cache-replay: unsupported provider %q", provider)
		}
	}
	return nil
}

func verifierEnvironment(extra []string) ([]string, error) {
	allowed := map[string]bool{"PATH": true, "LANG": true, "LC_ALL": true, "TMPDIR": true}
	blocked := map[string]bool{
		"OPENAI_API_KEY": true, "ANTHROPIC_API_KEY": true, "GEMINI_API_KEY": true,
		"AWS_BEARER_TOKEN_BEDROCK": true, "AWS_ACCESS_KEY_ID": true, "AWS_SECRET_ACCESS_KEY": true, "AWS_SESSION_TOKEN": true,
	}
	for _, name := range extra {
		name = strings.TrimSpace(name)
		if !validEnvironmentName(name) || blocked[name] {
			return nil, fmt.Errorf("cache-replay: verifier environment variable %q is invalid or credential-bearing", name)

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Export AWS_BEARER_TOKEN_BEDROCK (preferred) OR both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
  2. If credentials live in an AWS profile/session, materialize them into the env for the cache-replay process (e.g. via aws configure export-credentials or your SSO tooling)
  3. Check for half-written pairs: one of the two classic vars missing still fails

Example fix

# before
# only AWS_ACCESS_KEY_ID exported
cache-replay -execute ...

# after
export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
# or: export AWS_BEARER_TOKEN_BEDROCK=...
cache-replay -execute ...
Defensive patterns

Strategy: validation

Validate before calling

if providers["bedrock"] {
	bearer := os.Getenv("AWS_BEARER_TOKEN_BEDROCK") != ""
	classic := os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != ""
	if !bearer && !classic {
		return errors.New("provide AWS_BEARER_TOKEN_BEDROCK or both AWS access env vars")
	}
}

Prevention

When it happens

Trigger: Trace with provider "bedrock" records where AWS_BEARER_TOKEN_BEDROCK is unset AND at least one of AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY is empty. Setting only the access key ID without the secret also triggers it.

Common situations: Relying on an AWS profile file or SSO session instead of env vars (the tool only inspects env); bearer token named differently; key rotation script updated one of the two env vars; CI masking one variable due to a name collision.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/3a6ff241b46eb026. Report an issue: GitHub.