{"record":{"id":"bcc129c8b6ae0e9e","repo":"JuliusBrussee/caveman","slug":"awscreds-s-credential-expiry-w","errorCode":null,"errorMessage":"awscreds: %s credential expiry: %w","messagePattern":"awscreds: (.+?) credential expiry: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":628,"sourceCode":"\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.\nfunc parseExpiry(raw string) (time.Time, error) {\n\traw = strings.TrimSpace(raw)\n\tif raw == \"\" {\n\t\treturn time.Time{}, nil","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L610-L646","documentation":"credentialsFromJSON passes the parsed Expiration field to parseExpiry; if that cannot produce a valid time, the credential document's expiry is malformed and this wrapped error is returned. The library throws it because credentials without a parsable expiration cannot be safely cached or refreshed.","triggerScenarios":"fromContainer or fromIMDS receives credential JSON whose Expiration is missing, empty, not RFC3339/expected ISO8601 format, or zero-valued.","commonSituations":"A fake/placeholder metadata endpoint in local development returning incomplete JSON; a hand-rolled mock ECS agent with a wrong date format; an intercepted or corrupted response body; unusual custom AWS_EC2_METADATA_SERVICE_ENDPOINT implementations.","solutions":["Inspect the Expiration field in the endpoint's raw response; it must be an RFC3339 timestamp like 2026-01-02T15:04:05Z.","If using a custom/mock metadata endpoint, fix it to emit a valid RFC3339 Expiration.","If the body looks corrupted, check for proxy interference and retry against the real metadata service.","Update the library if the service changed its timestamp format (rare AWS-side format changes)."],"exampleFix":"// before (mock IMDS response)\n{\"AccessKeyID\":\"AKIA...\",\"SecretAccessKey\":\"...\",\"Token\":\"...\",\"Expiration\":\"tomorrow\"}\n// after\n{\"AccessKeyID\":\"AKIA...\",\"SecretAccessKey\":\"...\",\"Token\":\"...\",\"Expiration\":\"2026-09-21T00:00:00Z\"}","handlingStrategy":"validation","validationCode":"func hasRFC3339Expiration(body []byte) bool {\n    var p struct{ Expiration string `json:\"Expiration\"` }\n    if json.Unmarshal(body, &p) != nil { return false }\n    _, err := time.Parse(time.RFC3339, p.Expiration)\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":"creds, err := provider.Credentials(ctx)\nvar expiryErr *time.ParseError\nif errors.As(err, &expiryErr) {\n    return fmt.Errorf(\"metadata endpoint sent bad Expiration (%q): check mock/proxy\", expiryErr.Value)\n}","preventionTips":["Ensure mock/test metadata servers emit RFC3339 timestamps.","Never hand-edit metadata responses; regenerate them from the real service.","Cover credential parsing in unit tests with a golden RFC3339 expiration."],"tags":["aws","credentials","date-format","metadata-service"],"backgroundTag":"invalid-date-format","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"}