{"record":{"id":"63cd98268401960d","repo":"SonarSource/sonarqube","slug":"multiple-redirects-have-been-found-for-s","errorCode":null,"errorMessage":"Multiple redirects have been found for '%s'","messagePattern":"Multiple redirects have been found for '(.+?)'","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RedirectFilter.java","lineNumber":62,"sourceCode":"  @Override\n  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {\n    HttpServletRequest request = (HttpServletRequest) servletRequest;\n    HttpServletResponse response = (HttpServletResponse) servletResponse;\n    String path = extractPath(request);\n    Predicate<Redirect> match = redirect -> redirect.test(path);\n    List<Redirect> redirects = REDIRECTS.stream()\n      .filter(match)\n      .toList();\n\n    switch (redirects.size()) {\n      case 0:\n        chain.doFilter(request, response);\n        break;\n      case 1:\n        response.sendRedirect(redirects.get(0).apply(request));\n        break;\n      default:\n        throw new IllegalStateException(format(\"Multiple redirects have been found for '%s'\", path));\n    }\n  }\n\n  public static Redirect newSimpleRedirect(String from, String to) {\n    return new Redirect() {\n      @Override\n      public boolean test(String path) {\n        return from.equals(path);\n      }\n\n      @Override\n      public String apply(HttpServletRequest request) {\n        return format(\"%s%s\", request.getContextPath(), to);\n      }\n    };\n  }\n\n  @Override","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RedirectFilter.java#L44-L80","documentation":"RedirectFilter resolves the request path against a set of registered Redirect rules and applies exactly one redirect. If more than one rule matches the same path, the filter cannot choose deterministically and throws this IllegalStateException in doFilter's default branch.","triggerScenarios":"Two or more Redirect registrations whose matchers both match the incoming request path; doFilter then finds redirects.size() > 1.","commonSituations":"A plugin registering a redirect that collides with a built-in redirect (same 'from' path); overlapping glob/prefix matchers after adding a new redirect rule; duplicate registration of similar paths (e.g. '/doc' and '/doc/*').","solutions":["Find the colliding Redirect registrations and make their 'from' matchers mutually exclusive (narrow one pattern)","Remove or rename the plugin-registered redirect that duplicates a core redirect path","Log all registered redirect rules at startup to detect overlapping patterns before runtime"],"exampleFix":"// before\nRedirect newSimpleRedirect(\"/doc\", \"/documentation\");\nRedirect newSimpleRedirect(\"/doc/*\", \"/documentation/*\"); // both match /doc/x\n// after\nRedirect newSimpleRedirect(\"/doc\", \"/documentation\");\nRedirect newSimpleRedirect(\"/doc/**\", \"/documentation/**\"); // mutually exclusive patterns","handlingStrategy":"validation","validationCode":"Map<String, Long> hits = redirects.stream().collect(Collectors.groupingBy(r -> r.from, Collectors.counting()));\nList<String> dupes = hits.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).toList();\nif (!dupes.isEmpty()) throw new IllegalStateException(\"Overlapping redirects registered for: \" + dupes);","typeGuard":null,"tryCatchPattern":"try { chain.doFilter(req, res); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Multiple redirects have been found for\")) { log.error(\"Fix colliding redirect registrations for path \" + path); } throw e; }","preventionTips":["Keep redirect 'from' patterns mutually exclusive when registering Redirect rules","Audit plugin-registered redirects against core redirects at startup","Prefer exact-match redirect registration over broad prefixes that can overlap"],"tags":["http","routing","redirect"],"backgroundTag":"invalid-state-transition","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"}