{"record":{"id":"fad0077d14fd042c","repo":"JuliusBrussee/caveman","slug":"awscreds-sts-credential-expiry-w","errorCode":null,"errorMessage":"awscreds: sts credential expiry: %w","messagePattern":"awscreds: sts credential expiry: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":366,"sourceCode":"\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 {\n\t\treturn nil, fmt.Errorf(\"awscreds: sts credential expiry: %w\", err)\n\t}\n\treturn &result{\n\t\tcreds: awssig.Credentials{\n\t\t\tAccessKeyID:     strings.TrimSpace(c.AccessKeyID),\n\t\t\tSecretAccessKey: strings.TrimSpace(c.SecretAccessKey),\n\t\t\tSessionToken:    strings.TrimSpace(c.SessionToken),\n\t\t},\n\t\texpires: expires,\n\t\tsource:  \"web_identity\",\n\t}, nil\n}\n\n// stsErrorCode extracts the machine-readable code of an STS ErrorResponse. The\n// body itself is never returned: it can echo the web identity token.\nfunc stsErrorCode(body []byte) string {\n\tvar parsed struct {\n\t\tXMLName xml.Name `xml:\"ErrorResponse\"`\n\t\tError   struct {","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L348-L384","documentation":"After a successful STS AssumeRoleWithWebIdentity call, the response's Credentials.Expiration timestamp is parsed with parseExpiry. If that timestamp can't be parsed into a time (unexpected format or empty), the parse error is wrapped as \"awscreds: sts credential expiry\" because session expiry is needed to know when to refresh the credentials.","triggerScenarios":"xml.Unmarshal succeeded but parseExpiry(c.Expiration) fails: the STS-compatible endpoint returned an Expiration string not in the expected RFC3339/ISO8601 format, an empty element, or a locally-formatted date from a non-AWS STS implementation.","commonSituations":"Using a third-party/minio-style STS-compatible service that formats Expiration differently; a proxy rewriting the XML; an STS API version change; hand-rolled mock STS servers in dev/test emitting wrong date formats.","solutions":["Check the raw STS XML response's <Expiration> value; it should be RFC3339 e.g. 2026-09-20T12:00:00Z","If using a non-AWS STS-compatible endpoint, fix or upgrade it to emit RFC3339 timestamps, or point at real AWS STS","Add a pre-flight check against the endpoint in dev/test to catch format drift early","Capture and inspect the wrapped parse error (%w) to see the exact time.Parse layout that failed"],"exampleFix":"// before\n// mock STS returns <Expiration>09/20/2026 12:00PM</Expiration>\n// after\n// mock STS returns RFC3339:\n// <Expiration>2026-09-20T12:00:00Z</Expiration>","handlingStrategy":"try-catch","validationCode":"// preflight a dev/test STS-compatible endpoint for RFC3339 expiration\n// (integration check): issue one AssumeRoleWithWebIdentity and regex the XML\n// for <Expiration>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})</Expiration>","typeGuard":null,"tryCatchPattern":"creds, err := awscreds.Credentials(ctx, p)\nif err != nil && strings.Contains(err.Error(), \"sts credential expiry\") {\n    return fmt.Errorf(\"STS endpoint returned non-RFC3339 Expiration; use a compliant STS: %w\", err)\n}","preventionTips":["Ensure any STS-compatible (minio, mock) endpoint emits Expiration as RFC3339 UTC","Pin the STS API version your endpoint implements and test after upgrades","Capture the wrapped %w error to see the failing time layout during triage","Add a contract test against your STS endpoint checking the Expiration format"],"tags":["aws","sts","date","parsing"],"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"}