JuliusBrussee/caveman · error

awscreds: sts assume role with web identity: http

Error message

awscreds: sts assume role with web identity: http %d%s

What it means

fromWebIdentity requires HTTP 200 from STS. When AssumeRoleWithWebIdentity returns any other status, this error reports the status code plus stsErrorCode(body), which extracts the STS error code/type from the XML error payload (e.g. InvalidIdentityToken, ExpiredToken, AccessDenied).

Solutions

  1. Read the STS error code embedded in the message (InvalidIdentityToken, AccessDenied, ExpiredToken...) and address that specific cause
  2. Verify AWS_ROLE_ARN exists and its trust policy trusts the OIDC provider of the token; check the token's aud matches the provider's client ID
  3. Get a fresh token — restart the pod or re-mount the serviceaccount token if it expired
  4. Handle 429/5xx with retries and backoff; check STS regional endpoint correctness (sts.<region>.amazonaws.com vs global)

Example fix

// before
// AWS_ROLE_ARN=arn:aws:iam::123:role/wrong-role -> http 403 AccessDenied
// after
// ensure trust policy:
// "Principal": {"Federated": "arn:aws:iam::123:oidc-provider/oidc.eks.us-east-1.amazonaws.com"},
// condition on sub = system:serviceaccount:<ns>:<sa>
os.Setenv("AWS_ROLE_ARN", "arn:aws:iam::123:role/pod-role")
Defensive patterns

Strategy: try-catch

Validate before calling

// validate role ARN shape and token freshness before the exchange
if !strings.HasPrefix(os.Getenv("AWS_ROLE_ARN"), "arn:aws:iam::") { return errors.New("bad AWS_ROLE_ARN") }
tok, _ := os.ReadFile(os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE"))
parts := strings.Split(string(tok), ".")
if len(parts) != 3 { return errors.New("malformed identity token") }

Try / catch

creds, err := awscreds.Credentials(ctx, p)
if err != nil && strings.Contains(err.Error(), "sts assume role with web identity: http ") {
    switch {
    case strings.Contains(err.Error(), "InvalidIdentityToken"), strings.Contains(err.Error(), "ExpiredToken"):
        // refresh token / restart pod
    case strings.Contains(err.Error(), "AccessDenied"):
        // fix role trust policy / ARN
    default:
        // 429/5xx: retry with backoff
    }
}

Prevention

When it happens

Trigger: p.sts.Do succeeds but resp.StatusCode != 200: the web identity token is expired or invalid, the role ARN is wrong/not trusted by the OIDC provider, the audience (aud) doesn't match, or the request is malformed (400) / throttled (429/503).

Common situations: EKS pods whose serviceaccount token expired or whose OIDC provider thumbprint/audience is misconfigured; AWS_ROLE_ARN pointing to a role the token's issuer isn't trusted to assume; regional STS endpoint mismatch; throttling under high pod churn.

Related errors


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

Appendix: source

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

	}
	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"`
		} `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 {

View on GitHub (pinned to 3ee70a1026)