{"record":{"id":"380f2e07cc8cf81d","repo":"JuliusBrussee/caveman","slug":"awscreds-read-sts-response-w","errorCode":null,"errorMessage":"awscreds: read sts response: %w","messagePattern":"awscreds: read sts response: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":344,"sourceCode":"\t\t\"RoleSessionName\":  {sessionName},\n\t\t\"WebIdentityToken\": {token},\n\t\t\"DurationSeconds\":  {\"3600\"},\n\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}","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L326-L362","documentation":"The STS AssumeRoleWithWebIdentity response body is read with io.ReadAll(io.LimitReader(resp.Body, maxBody)). If reading the body fails mid-stream — connection reset, unexpected EOF, context canceled while streaming — the error is wrapped as \"awscreds: read sts response\" and the credential fetch fails.","triggerScenarios":"io.ReadAll inside fromWebIdentity returns err after p.sts.Do succeeded: server closed the connection mid-body, TLS truncation, proxy dropped the stream, or ctx was canceled during the read.","commonSituations":"Flaky networks/NAT dropping long-lived connections; load balancers in front of a self-hosted STS terminating connections; aggressive context timeouts cutting the read short; intermediate proxies mangling the response.","solutions":["Retry the credential fetch with backoff — body-read resets are usually transient","Extend the context deadline if ctx cancellation is the cause","Check for proxies/LBs between the client and STS that truncate responses; test with curl against the same endpoint","Ensure the STS endpoint is the official one (or a healthy compatible endpoint) rather than a flaky mirror"],"exampleFix":"// before\ncreds, err := awscreds.Credentials(ctx, p) // unexpected EOF during body read\n// after\nvar creds *awscreds.Result\nfor i := 0; i < 3; i++ {\n    creds, err = awscreds.Credentials(ctx, p)\n    if err == nil || !strings.Contains(err.Error(), \"read sts response\") { break }\n    time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)\n}","handlingStrategy":"retry","validationCode":"// no caller-side pre-check can prevent a mid-body reset; instead preflight the endpoint\nresp, err := http.Head(stsEndpoint)\nif err == nil { resp.Body.Close() } // ensures the path is at least reachable","typeGuard":"func isBodyReadFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"read sts response\")\n}","tryCatchPattern":"creds, err := awscreds.Credentials(ctx, p)\nif isBodyReadFailure(err) {\n    // transient stream break: one retry is usually enough\n    time.Sleep(250 * time.Millisecond)\n    creds, err = awscreds.Credentials(ctx, p)\n}","preventionTips":["Keep the context deadline longer than the expected STS round-trip","Remove/bypass proxies that truncate streaming bodies","Prefer official STS endpoints over mirrors behind flaky LBs","Retry idempotent credential fetches on transient IO errors"],"tags":["aws","sts","network","io"],"backgroundTag":"network-request-failed","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"}