grpc/grpc-go · error
decode error
Error message
decode error: %v
What it means
Returned by extractExpiration when base64.RawURLEncoding.DecodeString fails on the claims segment. The header/signature split was correct (two dots present) but the payload is not valid unpadded URL-safe base64. The %v is the base64 decoder error.
Solutions
- Regenerate the token with a JWT library that emits unpadded URL-safe base64 (the JWT spec default).
- Verify the payload segment length is a multiple of 4 after removing padding, or that it has no '='.
- Replace any '+'/'/' characters and strip '=' to test whether encoding is the cause.
Defensive patterns
Strategy: validation
Validate before calling
func claimsDecode(claimsSeg string) ([]byte, error) {
// Mirror the reader: RawURLEncoding (no padding).
return base64.RawURLEncoding.DecodeString(claimsSeg)
} Prevention
- Issue tokens with a standard JWT library that emits unpadded URL-safe base64.
- Reject tokens containing '=' padding or '+'/'/' characters at ingestion time.
- Log the failing segment length to spot truncation.
When it happens
Trigger: The payload segment contains characters outside the URL-safe base64 alphabet, has incorrect padding (the library uses RawURLEncoding so padding must be absent), or was corrupted/truncated.
Common situations: Token generated by a library that emits padded base64 (with '=') or standard base64 ('+'/'/') instead of URL-safe; manual editing of the token; encoding mismatch between issuer and gRPC jwt reader.
Related errors
- expected 3 parts in token
- unmarshal error
- credentials: audience cannot be empty
- no expiration claims
- token file
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/f7036b960fe05d0b.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/jwt/file_reader.go:98
if !ok { // only one period found
return "", false
}
_, _, ok = strings.Cut(s, tokenDelim)
if ok { // three periods found
return "", false
}
return claims, true
}
// 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")
}
View on GitHub (pinned to 0c51461d27)