grpc/grpc-go · error
expected 3 parts in token
Error message
expected 3 parts in token
What it means
extractClaimsRaw returns ok=false when the token does not have exactly two '.' delimiters producing three non-empty segments (file_reader.go:74-95). extractExpiration then returns 'expected 3 parts in token'. The reader requires a well-formed JWS compact serialization even though it only reads the payload.
Source
Thrown at credentials/jwt/file_reader.go:94
if !ok { // no period found
return "", false
}
claims, s, ok := strings.Cut(s, tokenDelim)
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.View on GitHub (pinned to 03255a9237)
Solutions
- Confirm the file content is a single-line compact JWT: header.payload.signature.
- Strip any internal whitespace/newlines before writing, not just leading/trailing.
- Point the reader at the actual ID token file, not an OAuth/refresh token or key file.
- Decode the string with a JWT tool to confirm the three-part structure.
Example fix
// before: stored a multi-line PEM-ish blob // -----BEGIN TOKEN----- // ey... // -----END TOKEN----- // after: store the compact serialization on one line eyJhbGciOi...<payload>...<sig>
Defensive patterns
Strategy: validation
Validate before calling
// Check the three-part structure before use.
parts := strings.Split(strings.TrimSpace(tok), ".")
if len(parts) != 3 {
return fmt.Errorf("token is not a 3-part JWT")
} Try / catch
_, _, err := r.readToken()
if err != nil && strings.Contains(err.Error(), "expected 3 parts in token") {
// token is not a compact JWT; replace the file contents.
return err
} Prevention
- Store the token as a single compact header.payload.signature line.
- Strip internal whitespace/newlines, not just leading/trailing.
- Use a real ID-token source rather than hand-edited strings.
- Validate the structure with a JWT tool before deploying.
When it happens
Trigger: The token string lacks dots, has only one dot, or has more than two dots. Caused by a non-JWT string, a JWT split across lines, trailing/leading whitespace inside the segments, or a token that was URL-encoded.
Common situations: Wrong file (raw key, refresh token, base64 blob), a token that was base64-encoded as a whole before being written, or whitespace/newlines in the middle of the file that TrimSpace at line 55 does not remove (it only trims ends).
Related errors
- credentials: audience cannot be empty
- token file %q: %v: %w
- decode error: %v
- unmarshal error: %v
- no expiration claims
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/316eecff58d0fd39.
Report an issue: GitHub.