grpc/grpc-go · error

unmarshal error

Error message

unmarshal error: %v

What it means

Returned by extractExpiration when json.Unmarshal of the decoded payload fails. The bytes decoded as base64 but are not valid JSON matching jwtClaims. The %v is the JSON decoder error. The reader only needs an `exp` field, so a well-formed JWT should never hit this; corruption or a non-JSON payload is the usual cause.

Solutions

  1. Confirm the token is a signed (JWS) JWT, not an encrypted (JWE) token; the gRPC reader cannot decrypt JWEs.
  2. Decode the payload manually (base64 -d) and confirm it is a JSON object containing an exp field.
  3. Re-issue the token through the intended ID-token flow.
Defensive patterns

Strategy: validation

Validate before calling

func claimsJSON(claimsSeg string) (map[string]any, error) {
    b, err := base64.RawURLEncoding.DecodeString(claimsSeg)
    if err != nil {
        return nil, err
    }
    var m map[string]any
    if err := json.Unmarshal(b, &m); err != nil {
        return nil, err
    }
    return m, nil
}

Prevention

When it happens

Trigger: The base64-decoded payload is not a JSON object (e.g. a JWE-encrypted token whose payload is ciphertext, a malformed token, or binary garbage that happened to be base64-decodable).

Common situations: Using an encrypted JWT (JWE) where a signed JWT (JWS) is expected; binary content inadvertently placed in the token file; character-set corruption.

Related errors


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

Appendix: source

Thrown at credentials/jwt/file_reader.go:103

		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")
	}

	return expTime, nil
}

View on GitHub (pinned to 0c51461d27)