grpc/grpc-go · error

expected 3 parts in token

Error message

expected 3 parts in token

What it means

Returned by extractClaimsRaw (via extractExpiration) when the token does not split into exactly three dot-separated parts (header.payload.signature). Without three parts the claims cannot be located, so parsing aborts. This is the most common JWT structural defect.

Solutions

  1. Confirm the file contains a single JWT with exactly two dots: printf '%s' "$TOKEN" | tr -cd '.' | wc -c should print 2.
  2. Switch the token source to one that emits a signed JWT (ID token) rather than an opaque access token.
  3. Strip any trailing newline/quotes from the file content.
Defensive patterns

Strategy: validation

Validate before calling

func isThreePartJWT(s string) bool {
    return strings.Count(s, ".") == 2
}

Type guard

func isJWT(v string) bool {
    s := strings.TrimSpace(v)
    return strings.Count(s, ".") == 2 && len(strings.Split(s, ".")[1]) > 0
}

Prevention

When it happens

Trigger: The token is an opaque bearer string, a JSON document, a base64 blob, or a JWT with extra/missing dots; copy-paste truncation that dropped the signature segment.

Common situations: Writing an OAuth access token instead of an ID token; newline or trailing characters breaking the split; token truncated by a log/env var length limit.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/316eecff58d0fd39. Report an issue: GitHub.

Appendix: 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 0c51461d27)