JuliusBrussee/caveman · error
awscreds: sts credential expiry
Error message
awscreds: sts credential expiry: %w
What it means
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.
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
Example fix
// before // mock STS returns <Expiration>09/20/2026 12:00PM</Expiration> // after // mock STS returns RFC3339: // <Expiration>2026-09-20T12:00:00Z</Expiration>
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight a dev/test STS-compatible endpoint for RFC3339 expiration
// (integration check): issue one AssumeRoleWithWebIdentity and regex the XML
// for <Expiration>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})</Expiration> Try / catch
creds, err := awscreds.Credentials(ctx, p)
if err != nil && strings.Contains(err.Error(), "sts credential expiry") {
return fmt.Errorf("STS endpoint returned non-RFC3339 Expiration; use a compliant STS: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- awscreds: build sts request
- awscreds: read sts response
- awscreds: sts assume role with web identity failed
- awscreds: sts assume role with web identity: http
- bedrock base url invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/fad0077d14fd042c.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/awscreds/awscreds.go:366
}
var parsed struct {
XMLName xml.Name `xml:"AssumeRoleWithWebIdentityResponse"`
Result struct {
Credentials struct {
AccessKeyID string `xml:"AccessKeyId"`
SecretAccessKey string `xml:"SecretAccessKey"`
SessionToken string `xml:"SessionToken"`
Expiration string `xml:"Expiration"`
} `xml:"Credentials"`
} `xml:"AssumeRoleWithWebIdentityResult"`
}
if err := xml.Unmarshal(body, &parsed); err != nil {
return nil, errors.New("awscreds: sts returned an unparseable response")
}
c := parsed.Result.Credentials
expires, err := parseExpiry(c.Expiration)
if err != nil {
return nil, fmt.Errorf("awscreds: sts credential expiry: %w", err)
}
return &result{
creds: awssig.Credentials{
AccessKeyID: strings.TrimSpace(c.AccessKeyID),
SecretAccessKey: strings.TrimSpace(c.SecretAccessKey),
SessionToken: strings.TrimSpace(c.SessionToken),
},
expires: expires,
source: "web_identity",
}, nil
}
// stsErrorCode extracts the machine-readable code of an STS ErrorResponse. The
// body itself is never returned: it can echo the web identity token.
func stsErrorCode(body []byte) string {
var parsed struct {
XMLName xml.Name `xml:"ErrorResponse"`
Error struct {View on GitHub (pinned to 3ee70a1026)