{"record":{"id":"8b5c6964e64f4322","repo":"JuliusBrussee/caveman","slug":"awscreds-s-returned-code-q","errorCode":null,"errorMessage":"awscreds: %s returned code %q","messagePattern":"awscreds: (.+?) returned code %q","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":624,"sourceCode":"\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 %s response: %w\", what, err)\n\t}\n\tif resp.StatusCode < 200 || resp.StatusCode > 299 {\n\t\treturn nil, fmt.Errorf(\"awscreds: %s: http %d\", what, resp.StatusCode)\n\t}\n\treturn body, nil\n}\n\nfunc credentialsFromJSON(body []byte, source string) (*result, error) {\n\tvar parsed credentialJSON\n\tif err := json.Unmarshal(body, &parsed); err != nil {\n\t\treturn nil, fmt.Errorf(\"awscreds: %s returned an unparseable response\", source)\n\t}\n\tif parsed.Code != \"\" && !strings.EqualFold(parsed.Code, \"Success\") {\n\t\treturn nil, fmt.Errorf(\"awscreds: %s returned code %q\", source, parsed.Code)\n\t}\n\texpires, err := parseExpiry(parsed.Expiration)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"awscreds: %s credential expiry: %w\", source, err)\n\t}\n\treturn &result{\n\t\tcreds: awssig.Credentials{\n\t\t\tAccessKeyID:     strings.TrimSpace(parsed.AccessKeyID),\n\t\t\tSecretAccessKey: strings.TrimSpace(parsed.SecretAccessKey),\n\t\t\tSessionToken:    strings.TrimSpace(parsed.Token),\n\t\t},\n\t\texpires: expires,\n\t\tsource:  source,\n\t}, nil\n}\n\n// parseExpiry maps an absent expiry to the zero time, which means \"never\n// refresh\" to the cache.","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L606-L642","documentation":"After successfully parsing the metadata JSON, credentialsFromJSON checks the optional 'Code' field. If it is present and not (case-insensitively) \"Success\", the metadata service reported an application-level error inside a 2xx response (e.g. \"BadRequest\", \"ExpiredToken\"), so the library refuses to return credentials. This catches IMDS/ECS responses where the error is carried in the body rather than the HTTP status.","triggerScenarios":"fromContainer or fromIMDS gets JSON whose Code field is set to something other than Success, e.g. the IMDS credentials document returns Code=\"BadRequest\" or an ECS agent error code embedded in a 200 response.","commonSituations":"Instance profile credentials not yet propagated right after attaching a role; ECS task role temporarily unavailable; requesting a role name that no longer exists while the service still returns 200 with an error code.","solutions":["Read the quoted code in the error message; look it up in AWS metadata-service error documentation (e.g. BadRequest means the request path/role is wrong).","For a just-attached instance profile, wait a few seconds and retry until credentials propagate.","Verify the role name used in the IMDS path matches a role listed at /latest/meta-data/iam/security-credentials/.","If persistent, re-check the task/instance IAM configuration in the AWS console."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"var probe struct{ Code string `json:\"Code\"` }\nif json.Unmarshal(body, &probe) == nil && probe.Code != \"\" && !strings.EqualFold(probe.Code, \"Success\") {\n    // treat as transient for BadRequest right after role attach\n}","typeGuard":null,"tryCatchPattern":"creds, err := provider.Credentials(ctx)\nif err != nil && strings.Contains(err.Error(), \"returned code\") {\n    // log code, backoff and retry a limited number of times\n    return retryWithBackoff(ctx, func() error { _, err = provider.Credentials(ctx); return err })\n}","preventionTips":["After attaching an instance profile, wait for propagation before first credential fetch.","Confirm the role name at /latest/meta-data/iam/security-credentials/ before requesting its credentials.","Keep task/instance IAM roles valid and not deleted mid-flight."],"tags":["aws","metadata-service","credentials","api-error"],"backgroundTag":"api-error-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"}