SonarSource/sonarqube · error · IllegalArgumentException

Can't URI decode

Error message

Can't URI decode: ${param}

What it means

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>'.

Solutions

  1. Percent-encode the filter value properly (encodeURIComponent / URLEncoder.encode) before sending
  2. Check the value has no bare '%' characters; every '%' must be followed by two hex digits
  3. Decode on the client side only once — avoid double-encoding
  4. Sanitize or strip characters that are not valid in the filter before calling the API

Example fix

// before
GET /api/...?filter=standard%zz=SECURITY
// after
const param = encodeURIComponent('standard=SECURITY'); // standard%3DSECURITY
Defensive patterns

Strategy: validation

Validate before calling

function isUriDecodable(s) {
  try { decodeURIComponent(s); return true; } catch { return false; }
}
// check every '%' is followed by two hex digits before calling the API

Try / catch

try {
  await api.searchIssues({ complianceStandards: rawFilter });
} catch (e) {
  if (e.status === 400 && /Can't URI decode/.test(e.message)) {
    throw new Error(`Malformed percent-encoding in filter '${rawFilter}'`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a complianceStandards filter containing an invalid percent-escape such as '%zz', a lone '%', or a truncated escape like 'M2'.

Common situations: 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.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/62fe3c4c7c1299b9. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/common/ParamParsingUtils.java:58

  public static Pair<SoftwareQuality, Severity> parseImpact(String impact) {
    String[] parts = impact.split("=");
    if (parts.length != 2) {
      throw new IllegalArgumentException("Invalid impact format: " + impact);
    }
    return Pair.of(SoftwareQuality.valueOf(parts[0]),
      Severity.valueOf(parts[1]));
  }

  public static Map<ReportKey, Set<String>> parseComplianceStandardsFilter(@Nullable String param) {
    if (param == null) {
      return Map.of();
    }

    String decodedParam;
    try {
      decodedParam = URLDecoder.decode(param, StandardCharsets.UTF_8);
    } catch (IllegalArgumentException e) {
      throw new IllegalArgumentException("Can't URI decode: " + param, e);
    }

    Map<ReportKey, Set<String>> categoriesByStandard = new HashMap<>();

    String[] parts = decodedParam.split("&");
    for (String part : parts) {
      String[] keyValue = part.split("=");
      if (keyValue.length != 2) {
        throw new IllegalArgumentException("Invalid format: " + decodedParam);
      }
      Set<String> values = Arrays.stream(keyValue[1].split(",")).filter(s -> !s.isBlank()).collect(Collectors.toSet());
      categoriesByStandard.put(ReportKey.parse(keyValue[0]), values);
    }

    return categoriesByStandard;
  }
}

View on GitHub (pinned to 184c821202)