grpc/grpc-go · error

unmarshal error: %v

Error message

unmarshal error: %v

What it means

After base64-decoding the claims segment, json.Unmarshal into jwtClaims fails and extractExpiration returns 'unmarshal error: %v' (file_reader.go:101-103). The decoded bytes are not valid JSON or not a JSON object containing the expected fields.

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 03255a9237)

Solutions

  1. Re-mint the token; do not hand-edit the payload.
  2. Decode the base64 payload yourself and pretty-print the JSON to find the syntax problem.
  3. Ensure the issuer emits canonical JSON (RFC 8259).
  4. If the payload legitimately contains fields jwtClaims ignores, that is fine — only structural errors fail Unmarshal.

Example fix

// before: payload decoded to malformed JSON
// {"exp": 1700000000,}

// after: re-issue so the payload is valid JSON
// {"iss":"...","exp":1700000000,"aud":"..."}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate that the decoded payload is a JSON object.
seg := strings.Split(tok, ".")[1]
b, err := base64.RawURLEncoding.DecodeString(seg)
if err != nil { return err }
if !json.Valid(b) {
    return fmt.Errorf("claims payload is not valid JSON")
}

Try / catch

_, _, err := r.readToken()
if err != nil && strings.Contains(err.Error(), "unmarshal error") {
    // payload corrupt: re-mint the token; do not hand-edit.
    return err
}

Prevention

When it happens

Trigger: The decoded payload is truncated, is a JSON array/string instead of an object, contains a syntax error, or uses a JSON encoding Go's encoding/json rejects (e.g. duplicate keys with strict handling, trailing commas).

Common situations: Corrupt token, a payload that was modified after issuance, a base64 bug that decoded to the wrong bytes, or a non-standard issuer producing JSON5/relaxed JSON.

Related errors


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