{"record":{"id":"f7036b960fe05d0b","repo":"grpc/grpc-go","slug":"decode-error-v","errorCode":null,"errorMessage":"decode error: %v","messagePattern":"decode error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":98,"sourceCode":"\tif !ok { // only one period found\n\t\treturn \"\", false\n\t}\n\t_, _, ok = strings.Cut(s, tokenDelim)\n\tif ok { // three periods found\n\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","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/jwt/file_reader.go#L80-L116","documentation":"After splitting out the claims segment, base64.RawURLEncoding.DecodeString fails and extractExpiration returns 'decode error: %v' (file_reader.go:96-98). The library expects unpadded URL-safe base64 (RFC 7515 JWS encoding); standard base64 or padded URL-safe base64 will not decode.","triggerScenarios":"The claims segment is not valid raw URL-safe base64: it uses '+'/'/' instead of '-','_', it has '=' padding, it contains non-base64 characters, or its length is wrong for the underlying JSON.","commonSituations":"A token minted by a library that emits padded base64 ('=' suffix), a token re-encoded through a tool that switched to standard alphabet, or manual editing that introduced illegal characters.","solutions":["Re-mint the token from a standards-compliant issuer so the payload is raw URL-safe base64 without padding.","If you control issuance, ensure the encoder uses base64.RawURLEncoding (Go) / base64url_NOPAD.","Do not re-encode or re-wrap the token after issuance.","Decode the middle segment manually with base64.RawURLEncoding to confirm before deploying."],"exampleFix":"// before: issuer padded the payload\n// header.cGF5bG9hZA==.sig\n\n// after: raw URL-safe, no padding\n// header.cGF5bG9hZA.sig\n// (re-issue from the token producer using base64.RawURLEncoding)","handlingStrategy":"validation","validationCode":"// Validate raw URL-safe base64 decode of the claims segment.\nseg := strings.Split(tok, \".\")[1]\nif _, err := base64.RawURLEncoding.DecodeString(seg); err != nil {\n    return fmt.Errorf(\"claims segment is not raw URL-safe base64: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"_, _, err := r.readToken()\nif err != nil && strings.Contains(err.Error(), \"decode error\") {\n    // issuer emitted padded or standard-base64 payload; re-mint token.\n    return err\n}","preventionTips":["Ensure issuers use base64.RawURLEncoding (no padding).","Do not re-encode or re-wrap tokens after issuance.","Decode the middle segment manually to confirm it is raw URL-safe base64.","Avoid tokens that pass through tools that switch base64 alphabets."],"tags":["grpc","jwt","base64","validation","parsing"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}