{"record":{"id":"efc9c44a10668fbb","repo":"JuliusBrussee/caveman","slug":"awscreds-sts-assume-role-with-web-identity-http-d-s","errorCode":null,"errorMessage":"awscreds: sts assume role with web identity: http %d%s","messagePattern":"awscreds: sts assume role with web identity: http (.+?)(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":347,"sourceCode":"\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, p.stsEndpoint, strings.NewReader(form.Encode()))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"awscreds: build sts request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Accept\", \"application/xml\")\n\tresp, err := p.sts.Do(req)\n\tif err != nil {\n\t\t// A transport error can carry the request URL but never the form body.\n\t\treturn nil, fmt.Errorf(\"awscreds: sts assume role with web identity failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxBody))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"awscreds: read sts response: %w\", err)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"awscreds: sts assume role with web identity: http %d%s\", resp.StatusCode, stsErrorCode(body))\n\t}\n\tvar parsed struct {\n\t\tXMLName xml.Name `xml:\"AssumeRoleWithWebIdentityResponse\"`\n\t\tResult  struct {\n\t\t\tCredentials struct {\n\t\t\t\tAccessKeyID     string `xml:\"AccessKeyId\"`\n\t\t\t\tSecretAccessKey string `xml:\"SecretAccessKey\"`\n\t\t\t\tSessionToken    string `xml:\"SessionToken\"`\n\t\t\t\tExpiration      string `xml:\"Expiration\"`\n\t\t\t} `xml:\"Credentials\"`\n\t\t} `xml:\"AssumeRoleWithWebIdentityResult\"`\n\t}\n\tif err := xml.Unmarshal(body, &parsed); err != nil {\n\t\treturn nil, errors.New(\"awscreds: sts returned an unparseable response\")\n\t}\n\tc := parsed.Result.Credentials\n\texpires, err := parseExpiry(c.Expiration)\n\tif err != nil {","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L329-L365","documentation":"fromWebIdentity requires HTTP 200 from STS. When AssumeRoleWithWebIdentity returns any other status, this error reports the status code plus stsErrorCode(body), which extracts the STS error code/type from the XML error payload (e.g. InvalidIdentityToken, ExpiredToken, AccessDenied).","triggerScenarios":"p.sts.Do succeeds but resp.StatusCode != 200: the web identity token is expired or invalid, the role ARN is wrong/not trusted by the OIDC provider, the audience (aud) doesn't match, or the request is malformed (400) / throttled (429/503).","commonSituations":"EKS pods whose serviceaccount token expired or whose OIDC provider thumbprint/audience is misconfigured; AWS_ROLE_ARN pointing to a role the token's issuer isn't trusted to assume; regional STS endpoint mismatch; throttling under high pod churn.","solutions":["Read the STS error code embedded in the message (InvalidIdentityToken, AccessDenied, ExpiredToken...) and address that specific cause","Verify AWS_ROLE_ARN exists and its trust policy trusts the OIDC provider of the token; check the token's aud matches the provider's client ID","Get a fresh token — restart the pod or re-mount the serviceaccount token if it expired","Handle 429/5xx with retries and backoff; check STS regional endpoint correctness (sts.<region>.amazonaws.com vs global)"],"exampleFix":"// before\n// AWS_ROLE_ARN=arn:aws:iam::123:role/wrong-role -> http 403 AccessDenied\n// after\n// ensure trust policy:\n// \"Principal\": {\"Federated\": \"arn:aws:iam::123:oidc-provider/oidc.eks.us-east-1.amazonaws.com\"},\n// condition on sub = system:serviceaccount:<ns>:<sa>\nos.Setenv(\"AWS_ROLE_ARN\", \"arn:aws:iam::123:role/pod-role\")","handlingStrategy":"try-catch","validationCode":"// validate role ARN shape and token freshness before the exchange\nif !strings.HasPrefix(os.Getenv(\"AWS_ROLE_ARN\"), \"arn:aws:iam::\") { return errors.New(\"bad AWS_ROLE_ARN\") }\ntok, _ := os.ReadFile(os.Getenv(\"AWS_WEB_IDENTITY_TOKEN_FILE\"))\nparts := strings.Split(string(tok), \".\")\nif len(parts) != 3 { return errors.New(\"malformed identity token\") }","typeGuard":null,"tryCatchPattern":"creds, err := awscreds.Credentials(ctx, p)\nif err != nil && strings.Contains(err.Error(), \"sts assume role with web identity: http \") {\n    switch {\n    case strings.Contains(err.Error(), \"InvalidIdentityToken\"), strings.Contains(err.Error(), \"ExpiredToken\"):\n        // refresh token / restart pod\n    case strings.Contains(err.Error(), \"AccessDenied\"):\n        // fix role trust policy / ARN\n    default:\n        // 429/5xx: retry with backoff\n    }\n}","preventionTips":["Verify the OIDC provider, thumbprint, and audience (aud) match the serviceaccount token","Ensure AWS_ROLE_ARN's trust policy federates the correct OIDC provider and sub condition","Handle 429/5xx with exponential backoff in callers","Use the regional STS endpoint consistent with your cluster setup"],"tags":["aws","sts","http","authentication"],"backgroundTag":"http-non-200-response","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}