{"record":{"id":"5f7d97131c145bc4","repo":"JuliusBrussee/caveman","slug":"bedrock-malformed-aws-credentials-want-accesskey","errorCode":null,"errorMessage":"bedrock: malformed AWS credentials (want accessKeyId:secretAccessKey[:sessionToken])","messagePattern":"bedrock: malformed AWS credentials \\(want accessKeyId:secretAccessKey\\[:sessionToken\\]\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/bedrock/signing.go","lineNumber":174,"sourceCode":"\t\tif strings.HasPrefix(key, \"AKIA\") || strings.HasPrefix(key, \"ASIA\") {\n\t\t\treturn \"aws_access_keys\", nil\n\t\t}\n\t\treturn \"bedrock_api_key\", nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"bedrock: unsupported credential auth kind\")\n\t}\n}\n\n// parseAWSCredentials decodes the \"accessKeyId:secretAccessKey[:sessionToken]\"\n// form carried in x-cave-upstream-key into awssig.Credentials. It fails closed:\n// a missing access key or secret is an error, never an unsigned passthrough.\nfunc parseAWSCredentials(raw string) (awssig.Credentials, error) {\n\tif raw == \"\" {\n\t\treturn awssig.Credentials{}, fmt.Errorf(\"bedrock: missing AWS credentials in x-cave-upstream-key\")\n\t}\n\tparts := strings.SplitN(raw, \":\", 3)\n\tif len(parts) < 2 || parts[0] == \"\" || parts[1] == \"\" {\n\t\treturn awssig.Credentials{}, fmt.Errorf(\"bedrock: malformed AWS credentials (want accessKeyId:secretAccessKey[:sessionToken])\")\n\t}\n\tcreds := awssig.Credentials{AccessKeyID: parts[0], SecretAccessKey: parts[1]}\n\tif len(parts) == 3 {\n\t\tcreds.SessionToken = parts[2]\n\t}\n\treturn creds, nil\n}\n\n// copyIfPresent copies a header from src to dst when present (case-insensitive).\nfunc copyIfPresent(dst, src http.Header, name string) {\n\tif values := src.Values(name); len(values) > 0 {\n\t\tfor _, v := range values {\n\t\t\tdst.Add(name, v)\n\t\t}\n\t}\n}\n","sourceCodeStart":156,"sourceCodeEnd":191,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/bedrock/signing.go#L156-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nexport CAVE_UPSTREAM_KEY=\"AKIAIOSFODNN7EXAMPLE\"\n\n# after\nexport CAVE_UPSTREAM_KEY=\"AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"","handlingStrategy":"validation","validationCode":"// validColonFormIAM mirrors parseAWSCredentials' acceptance check.\nfunc validColonFormIAM(raw string) bool {\n    parts := strings.SplitN(raw, \":\", 3)\n    return len(parts) >= 2 && parts[0] != \"\" && parts[1] != \"\"\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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."],"tags":["bedrock","authentication","credentials","aws","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}