grpc/grpc-go · error

no expiration claims

Error message

no expiration claims

What it means

After unmarshalling, if claims.Exp == 0 (the zero value for int64) extractExpiration returns 'no expiration claims' (file_reader.go:106-108). The reader requires an exp claim because it needs to know when to consider the token dead; a JWT without exp is unusable for this credential.

Source

Thrown at credentials/jwt/file_reader.go:107

// extractExpiration parses the JWT token to extract the expiration time.
func (r *jwtFileReader) extractExpiration(token string) (time.Time, error) {
	claimsRaw, ok := extractClaimsRaw(token)
	if !ok {
		return time.Time{}, fmt.Errorf("expected 3 parts in token")
	}
	payloadBytes, err := base64.RawURLEncoding.DecodeString(claimsRaw)
	if err != nil {
		return time.Time{}, fmt.Errorf("decode error: %v", err)
	}

	var claims jwtClaims
	if err := json.Unmarshal(payloadBytes, &claims); err != nil {
		return time.Time{}, fmt.Errorf("unmarshal error: %v", err)
	}

	if claims.Exp == 0 {
		return time.Time{}, fmt.Errorf("no expiration claims")
	}

	expTime := time.Unix(claims.Exp, 0)

	// Check if token is already expired.
	if expTime.Before(time.Now()) {
		return time.Time{}, fmt.Errorf("expired token")
	}

	return expTime, nil
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Configure the issuer to include a real exp (future Unix timestamp).
  2. If using a custom claims builder, ensure exp is set and non-zero.
  3. Switch to an ID-token source that always emits exp (GCP metadata server, OIDC provider).

Example fix

// before: token minted without exp
// {"iss":"svc","aud":"api"}

// after: issuer includes exp
// {"iss":"svc","aud":"api","exp":1735689600}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm exp is present and positive before use.
var c struct{ Exp int64 `json:"exp"` }
_ = json.Unmarshal(payloadBytes, &c)
if c.Exp == 0 {
    return errors.New("token has no exp claim")
}

Try / catch

_, _, err := r.readToken()
if err != nil && strings.Contains(err.Error(), "no expiration claims") {
    // issuer dropped exp; reconfigure the issuer to include it.
    return err
}

Prevention

When it happens

Trigger: The JWT payload has no exp field, exp is absent, or the issuer emitted exp:0. Because jwtClaims only decodes exp, any other claim structure is fine — but exp must be a positive Unix timestamp.

Common situations: A token minted by an issuer configured without expiration, a misconfigured OIDC provider, or a service-account token minted with a custom claims override that dropped exp.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/eb549c68923f7b47. Report an issue: GitHub.