JuliusBrussee/caveman · error
bedrock: malformed AWS credentials (want accessKeyId:secretA
Error message
bedrock: malformed AWS credentials (want accessKeyId:secretAccessKey[:sessionToken])
What it means
Thrown by parseAWSCredentials when the credential string does not contain at least a non-empty accessKeyId and secretAccessKey separated by a colon. The parser splits on ':' with SplitN(raw, ":", 3), so anything with fewer than 2 parts, or an empty first/second part, is rejected before signing — fail-closed rather than signing with partial keys.
Source
Thrown at proxy/providers/bedrock/signing.go:174
if strings.HasPrefix(key, "AKIA") || strings.HasPrefix(key, "ASIA") {
return "aws_access_keys", nil
}
return "bedrock_api_key", nil
default:
return "", fmt.Errorf("bedrock: unsupported credential auth kind")
}
}
// parseAWSCredentials decodes the "accessKeyId:secretAccessKey[:sessionToken]"
// form carried in x-cave-upstream-key into awssig.Credentials. It fails closed:
// a missing access key or secret is an error, never an unsigned passthrough.
func parseAWSCredentials(raw string) (awssig.Credentials, error) {
if raw == "" {
return awssig.Credentials{}, fmt.Errorf("bedrock: missing AWS credentials in x-cave-upstream-key")
}
parts := strings.SplitN(raw, ":", 3)
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
return awssig.Credentials{}, fmt.Errorf("bedrock: malformed AWS credentials (want accessKeyId:secretAccessKey[:sessionToken])")
}
creds := awssig.Credentials{AccessKeyID: parts[0], SecretAccessKey: parts[1]}
if len(parts) == 3 {
creds.SessionToken = parts[2]
}
return creds, nil
}
// copyIfPresent copies a header from src to dst when present (case-insensitive).
func copyIfPresent(dst, src http.Header, name string) {
if values := src.Values(name); len(values) > 0 {
for _, v := range values {
dst.Add(name, v)
}
}
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Format the credential as accessKeyId:secretAccessKey with both parts non-empty, e.g. "AKIA...:wJalr...".
- Quote the value in shell/YAML so colons and special characters in the secret survive intact.
- If a session token is used, append it as the third segment: "accessKeyId:secret:sessionToken" — but never put the token before the secret.
Example fix
# before export CAVE_UPSTREAM_KEY="AKIAIOSFODNN7EXAMPLE" # after export CAVE_UPSTREAM_KEY="AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
Defensive patterns
Strategy: validation
Validate before calling
// validColonFormIAM mirrors parseAWSCredentials' acceptance check.
func validColonFormIAM(raw string) bool {
parts := strings.SplitN(raw, ":", 3)
return len(parts) >= 2 && parts[0] != "" && parts[1] != ""
} Try / catch
Catch at credential resolution and report as a configuration error naming which part is missing; never retry and never send the partial value as a bearer token.
Prevention
- Store accessKeyId, secretAccessKey, and sessionToken as separate fields and join with ':' only at the boundary.
- Never assume the colon form in hand-written notes survived shell interpolation — quote it.
- Add a unit test that feeds representative key shapes (AKIA-only, empty secret) through your credential loader.
When it happens
Trigger: Key strings like "AKIA..." (no colon, no secret), "AKIA...:" (empty secret), ":secret" (empty access key), or a secret containing colons where the access key was omitted. Also plain Bedrock API keys that happen to reach this parser with no colon at all.
Common situations: Pasting only the access key ID and forgetting the secret; a YAML/env value where the trailing "<secret>" got stripped by shell redirection (unquoted value ending in special chars); a secret-management template that rendered only one of the two fields.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- bedrock: missing AWS credentials in x-cave-upstream-key
- bedrock: unsupported credential auth kind
- cave_sandbox_credential_capability_ambiguous
- {name} must be an absolute http(s) URL without credentials
- cache-replay: Bedrock bearer token or AWS access credentials
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/5f7d97131c145bc4.
Report an issue: GitHub.