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
- Refresh the token file with a current JWT (re-mint or let the rotating sidecar/kubelet repopulate it).
- Ensure the file-reader is pointed at a path that is continuously refreshed (e.g. projected service-account token volume with rotation).
- Sync clocks (NTP/chrony) to rule out skew before treating the token as genuinely expired.
- 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
- Point the reader at a continuously-rotated token file (projected SA token volume).
- Re-read the file on each use rather than caching the first read.
- Sync clocks with NTP to rule out skew.
- Avoid committing long-lived static tokens to repos or images.
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- credentials: audience cannot be empty
- %v: %w
- token file %q is empty: %w
- token file %q: %v: %w
- no expiration claims
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/94e06a111b5aef82.
Report an issue: GitHub.