{"record":{"id":"62fe3c4c7c1299b9","repo":"SonarSource/sonarqube","slug":"can-t-uri-decode-param","errorCode":null,"errorMessage":"Can't URI decode: ${param}","messagePattern":"Can't URI decode: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/common/ParamParsingUtils.java","lineNumber":58,"sourceCode":"  public static Pair<SoftwareQuality, Severity> parseImpact(String impact) {\n    String[] parts = impact.split(\"=\");\n    if (parts.length != 2) {\n      throw new IllegalArgumentException(\"Invalid impact format: \" + impact);\n    }\n    return Pair.of(SoftwareQuality.valueOf(parts[0]),\n      Severity.valueOf(parts[1]));\n  }\n\n  public static Map<ReportKey, Set<String>> parseComplianceStandardsFilter(@Nullable String param) {\n    if (param == null) {\n      return Map.of();\n    }\n\n    String decodedParam;\n    try {\n      decodedParam = URLDecoder.decode(param, StandardCharsets.UTF_8);\n    } catch (IllegalArgumentException e) {\n      throw new IllegalArgumentException(\"Can't URI decode: \" + param, e);\n    }\n\n    Map<ReportKey, Set<String>> categoriesByStandard = new HashMap<>();\n\n    String[] parts = decodedParam.split(\"&\");\n    for (String part : parts) {\n      String[] keyValue = part.split(\"=\");\n      if (keyValue.length != 2) {\n        throw new IllegalArgumentException(\"Invalid format: \" + decodedParam);\n      }\n      Set<String> values = Arrays.stream(keyValue[1].split(\",\")).filter(s -> !s.isBlank()).collect(Collectors.toSet());\n      categoriesByStandard.put(ReportKey.parse(keyValue[0]), values);\n    }\n\n    return categoriesByStandard;\n  }\n}\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/common/ParamParsingUtils.java#L40-L76","documentation":"parseComplianceStandardsFilter URL-decodes its input with URLDecoder.decode before parsing key/value pairs. If the raw parameter contains malformed percent-encoding (e.g. a trailing '%' or invalid hex sequence), URLDecoder throws IllegalArgumentException, which is rethrown as 'Can't URI decode: <param>'.","triggerScenarios":"Passing a complianceStandards filter containing an invalid percent-escape such as '%zz', a lone '%', or a truncated escape like 'M2'.","commonSituations":"Double- or under-encoding query parameters when building requests by hand; copying values out of logs where '+' and '%' were mangled; a proxy stripping part of the encoded value.","solutions":["Percent-encode the filter value properly (encodeURIComponent / URLEncoder.encode) before sending","Check the value has no bare '%' characters; every '%' must be followed by two hex digits","Decode on the client side only once — avoid double-encoding","Sanitize or strip characters that are not valid in the filter before calling the API"],"exampleFix":"// before\nGET /api/...?filter=standard%zz=SECURITY\n// after\nconst param = encodeURIComponent('standard=SECURITY'); // standard%3DSECURITY","handlingStrategy":"validation","validationCode":"function isUriDecodable(s) {\n  try { decodeURIComponent(s); return true; } catch { return false; }\n}\n// check every '%' is followed by two hex digits before calling the API","typeGuard":null,"tryCatchPattern":"try {\n  await api.searchIssues({ complianceStandards: rawFilter });\n} catch (e) {\n  if (e.status === 400 && /Can't URI decode/.test(e.message)) {\n    throw new Error(`Malformed percent-encoding in filter '${rawFilter}'`);\n  }\n  throw e;\n}","preventionTips":["Always percent-encode filter parameters with encodeURIComponent/URLEncoder.encode","Never manually splice already-encoded values (avoid double encoding)","Round-trip test decode(encode(x)) === x in tests","Strip stray '%' characters from user input"],"tags":["java","parameter-parsing","url-encoding"],"backgroundTag":"invalid-url-format","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}