grpc/grpc-go · error

expired token

Error message

expired token

What it means

After extracting exp, if time.Unix(claims.Exp,0).Before(time.Now()) extractExpiration returns 'expired token' (file_reader.go:110-114). The token was structurally valid but its exp claim is in the past, so the credential refuses to use it.

Source

Thrown at credentials/jwt/file_reader.go:114

	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. Refresh the token file with a current JWT (re-mint or let the rotating sidecar/kubelet repopulate it).
  2. Ensure the file-reader is pointed at a path that is continuously refreshed (e.g. projected service-account token volume with rotation).
  3. Sync clocks (NTP/chrony) to rule out skew before treating the token as genuinely expired.
  4. For long-lived processes, re-read the file on each use rather than caching the first read.

Example fix

// before: one-shot read of a token that later expired
tok, exp, err := r.readToken() // cached forever

// after: re-read before each use so rotations are picked up
tok, exp, err := r.readToken()
if err != nil { return err } // forces a fresh file read next call
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check expiry against a clock so the credential is not handed an already-dead token.
exp, err := extractExp(tok)
if err != nil { return err }
if !exp.After(time.Now()) {
    return errors.New("token already expired")
}

Try / catch

tok, _, err := r.readToken()
if err != nil && strings.Contains(err.Error(), "expired token") {
    // refresh the token file (sidecar/kubelet rotation) and retry.
    return err
}

Prevention

When it happens

Trigger: The token file holds a JWT whose exp already passed. Common with long-running processes that read a token once and never refresh, a token-rotating sidecar that stopped updating the file, or a static token captured for debugging.

Common situations: Projected token volume rotation disabled or delayed, a CI job using a hardcoded token committed long ago, clock skew where the local clock is ahead, or a short-lived token read after its lifetime elapsed.

Understand the failure class

Related errors


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