{"record":{"id":"f40c05f109823c87","repo":"grpc/grpc-go","slug":"unmarshal-error-v","errorCode":null,"errorMessage":"unmarshal error: %v","messagePattern":"unmarshal error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":103,"sourceCode":"\t\treturn \"\", false\n\t}\n\treturn claims, true\n}\n\n// extractExpiration parses the JWT token to extract the expiration time.\nfunc (r *jwtFileReader) extractExpiration(token string) (time.Time, error) {\n\tclaimsRaw, ok := extractClaimsRaw(token)\n\tif !ok {\n\t\treturn time.Time{}, fmt.Errorf(\"expected 3 parts in token\")\n\t}\n\tpayloadBytes, err := base64.RawURLEncoding.DecodeString(claimsRaw)\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"decode error: %v\", err)\n\t}\n\n\tvar claims jwtClaims\n\tif err := json.Unmarshal(payloadBytes, &claims); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"unmarshal error: %v\", err)\n\t}\n\n\tif claims.Exp == 0 {\n\t\treturn time.Time{}, fmt.Errorf(\"no expiration claims\")\n\t}\n\n\texpTime := time.Unix(claims.Exp, 0)\n\n\t// Check if token is already expired.\n\tif expTime.Before(time.Now()) {\n\t\treturn time.Time{}, fmt.Errorf(\"expired token\")\n\t}\n\n\treturn expTime, nil\n}\n","sourceCodeStart":85,"sourceCodeEnd":119,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/jwt/file_reader.go#L85-L119","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Re-mint the token; do not hand-edit the payload.","Decode the base64 payload yourself and pretty-print the JSON to find the syntax problem.","Ensure the issuer emits canonical JSON (RFC 8259).","If the payload legitimately contains fields jwtClaims ignores, that is fine — only structural errors fail Unmarshal."],"exampleFix":"// before: payload decoded to malformed JSON\n// {\"exp\": 1700000000,}\n\n// after: re-issue so the payload is valid JSON\n// {\"iss\":\"...\",\"exp\":1700000000,\"aud\":\"...\"}","handlingStrategy":"validation","validationCode":"// Pre-validate that the decoded payload is a JSON object.\nseg := strings.Split(tok, \".\")[1]\nb, err := base64.RawURLEncoding.DecodeString(seg)\nif err != nil { return err }\nif !json.Valid(b) {\n    return fmt.Errorf(\"claims payload is not valid JSON\")\n}","typeGuard":null,"tryCatchPattern":"_, _, err := r.readToken()\nif err != nil && strings.Contains(err.Error(), \"unmarshal error\") {\n    // payload corrupt: re-mint the token; do not hand-edit.\n    return err\n}","preventionTips":["Never hand-edit JWT payloads.","Decode and pretty-print the payload to spot JSON errors.","Ensure issuers emit canonical JSON.","Re-mint corrupt tokens from a trusted source."],"tags":["grpc","jwt","json","validation","parsing"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}