JuliusBrussee/caveman · error

awscreds: sts assume role with web identity failed

Error message

awscreds: sts assume role with web identity failed: %w

What it means

After building the AssumeRoleWithWebIdentity request, fromWebIdentity executes it via the STS HTTP client. A transport-level failure (DNS, TCP connect, TLS, timeout, context cancellation) is wrapped with this message. The library is careful that the wrapped transport error carries the request URL but never the form body, so the web identity token is not leaked into the error text.

Solutions

  1. Confirm network egress to the STS endpoint (curl -v https://sts.<region>.amazonaws.com) from the same environment; add a VPC endpoint for STS if egress is blocked
  2. Unwrap with errors.As(*url.Error) to see the underlying cause (timeout vs DNS vs TLS) and fix accordingly
  3. Check proxy env vars (HTTPS_PROXY) and certificate bundles that could break TLS
  4. Retry with backoff for transient timeouts; verify the context deadline isn't too short

Example fix

// before
creds, err := awscreds.Credentials(ctx, p) // transport error, ctx has 2s deadline
// after
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
creds, err := awscreds.Credentials(ctx, p)
if err != nil {
    var ue *url.Error
    if errors.As(err, &ue) { log.Printf("sts transport: %v", ue.Err) }
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight connectivity before credential fetch
conn, err := net.DialTimeout("tcp", "sts.amazonaws.com:443", 3*time.Second)
if err != nil { return fmt.Errorf("no egress to STS: %w", err) }
conn.Close()

Type guard

func isTransportFailure(err error) bool {
    var ue *url.Error
    return errors.As(err, &ue) && strings.Contains(err.Error(), "sts assume role with web identity failed")
}

Try / catch

var creds *awscreds.Result
var err error
for attempt := 0; attempt < 3; attempt++ {
    creds, err = awscreds.Credentials(ctx, p)
    if err == nil || !isTransportFailure(err) { break }
    select {
    case <-time.After(time.Duration(1<<attempt) * 200 * time.Millisecond):
    case <-ctx.Done():
        return ctx.Err()
    }
}

Prevention

When it happens

Trigger: p.sts.Do(req) returns a *url.Error: STS endpoint unreachable, DNS failure, TLS handshake failure, request timeout, or ctx canceled mid-request while assuming a role via AWS_WEB_IDENTITY_TOKEN_FILE/AWS_ROLE_ARN.

Common situations: EKS pods without network egress to sts.<region>.amazonaws.com (missing VPC endpoint or NAT); corporate proxy blocking the call; STS regional endpoint typo; DNS failures in restricted clusters; slow IMDS-adjacent setups causing context timeouts.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/41524631e34861b3. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/awscreds/awscreds.go:339

	}
	form := url.Values{
		"Action":           {"AssumeRoleWithWebIdentity"},
		"Version":          {"2011-06-15"},
		"RoleArn":          {roleARN},
		"RoleSessionName":  {sessionName},
		"WebIdentityToken": {token},
		"DurationSeconds":  {"3600"},
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.stsEndpoint, strings.NewReader(form.Encode()))
	if err != nil {
		return nil, fmt.Errorf("awscreds: build sts request: %w", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", "application/xml")
	resp, err := p.sts.Do(req)
	if err != nil {
		// A transport error can carry the request URL but never the form body.
		return nil, fmt.Errorf("awscreds: sts assume role with web identity failed: %w", err)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(io.LimitReader(resp.Body, maxBody))
	if err != nil {
		return nil, fmt.Errorf("awscreds: read sts response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("awscreds: sts assume role with web identity: http %d%s", resp.StatusCode, stsErrorCode(body))
	}
	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"`

View on GitHub (pinned to 3ee70a1026)