{"record":{"id":"2dc31abb07fc0f12","repo":"grpc/grpc-java","slug":"invalid-character-in-what-at-index-i","errorCode":null,"errorMessage":"Invalid character in ${what} at index ${i}","messagePattern":"Invalid character in (.+?) at index (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/io/grpc/Uri.java","lineNumber":1075,"sourceCode":"          throw new IllegalArgumentException(\n              \"Invalid percent-encoding at index \" + i + \" of \" + what + \": \" + s);\n        }\n        int h1 = Character.digit(s.charAt(i + 1), 16);\n        int h2 = Character.digit(s.charAt(i + 2), 16);\n        if (h1 == -1 || h2 == -1) {\n          throw new IllegalArgumentException(\n              \"Invalid hex digit in \" + what + \" at index \" + i + \" of: \" + s);\n        }\n        if (outBuf != null) {\n          outBuf.put((byte) (h1 << 4 | h2));\n        }\n        i += 2;\n      } else if (allowedChars == null || allowedChars.get(c)) {\n        if (outBuf != null) {\n          outBuf.put((byte) c);\n        }\n      } else {\n        throw new IllegalArgumentException(\"Invalid character in \" + what + \" at index \" + i);\n      }\n    }\n  }\n\n  @Nullable\n  private static String percentDecodeAssumedUtf8(@Nullable String s) {\n    if (s == null || s.indexOf('%') == -1) {\n      return s;\n    }\n\n    ByteBuffer utf8Bytes = percentDecode(s);\n    try {\n      return StandardCharsets.UTF_8\n          .newDecoder()\n          .onMalformedInput(CodingErrorAction.REPLACE)\n          .onUnmappableCharacter(CodingErrorAction.REPLACE)\n          .decode(utf8Bytes)\n          .toString();","sourceCodeStart":1057,"sourceCodeEnd":1093,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/api/src/main/java/io/grpc/Uri.java#L1057-L1093","documentation":"Thrown during percent-decoding when a plain (non-'%') character is not in the allowed-char set for the component being validated. Each URI component (path, query, fragment, etc.) has its own BitSet of permitted characters; anything else is rejected.","triggerScenarios":"Setting a Uri component with characters illegal for that component, e.g. spaces, '<', '>', '\"', '{', '}', '|' or non-ASCII characters in a path/query passed through a validating setter.","commonSituations":"Raw user input or file paths placed directly into a URI path; Unicode identifiers in hostnames/paths; spaces in query values that were never encoded.","solutions":["Percent-encode disallowed characters before setting the value (space -> %20, etc.)","Use a standard encoder per component (URLEncoder for query values, URI path encoding for paths)","Restrict or normalize input (e.g. trim, ASCII-fold) before building the URI","Check which component you are setting and its allowed-char rules"],"exampleFix":"// before\nbuilder.setPath(\"/docs/chapter 1\");\n// after\nbuilder.setPath(\"/docs/\" + URLEncoder.encode(\"chapter 1\", StandardCharsets.UTF_8));","handlingStrategy":"validation","validationCode":"static String encodeComponent(String raw) {\n  return URLEncoder.encode(raw, StandardCharsets.UTF_8); // spaces->+, use path encoder for paths\n}","typeGuard":null,"tryCatchPattern":"try { builder.setPath(userPath); } catch (IllegalArgumentException e) { builder.setPath(URLEncoder.encode(userPath, StandardCharsets.UTF_8)); }","preventionTips":["Percent-encode any user-controlled value before placing it in a URI","Use the right encoder per component (query vs path)","Strip/normalize illegal characters (spaces, <, >, |, unicode) early","Keep raw file paths out of URI paths — encode first"],"tags":["uri","validation","encoding"],"backgroundTag":"invalid-url-format","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}