SonarSource/sonarqube · error · IllegalStateException

Multiple redirects have been found for

Error message

Multiple redirects have been found for '%s'

What it means

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.

Solutions

  1. Find the colliding Redirect registrations and make their 'from' matchers mutually exclusive (narrow one pattern)
  2. Remove or rename the plugin-registered redirect that duplicates a core redirect path
  3. Log all registered redirect rules at startup to detect overlapping patterns before runtime

Example fix

// before
Redirect newSimpleRedirect("/doc", "/documentation");
Redirect newSimpleRedirect("/doc/*", "/documentation/*"); // both match /doc/x
// after
Redirect newSimpleRedirect("/doc", "/documentation");
Redirect newSimpleRedirect("/doc/**", "/documentation/**"); // mutually exclusive patterns
Defensive patterns

Strategy: validation

Validate before calling

Map<String, Long> hits = redirects.stream().collect(Collectors.groupingBy(r -> r.from, Collectors.counting()));
List<String> dupes = hits.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).toList();
if (!dupes.isEmpty()) throw new IllegalStateException("Overlapping redirects registered for: " + dupes);

Try / catch

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; }

Prevention

When it happens

Trigger: Two or more Redirect registrations whose matchers both match the incoming request path; doFilter then finds redirects.size() > 1.

Common situations: 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/*').

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at server/sonar-webserver/src/main/java/org/sonar/server/platform/web/RedirectFilter.java:62

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    String path = extractPath(request);
    Predicate<Redirect> match = redirect -> redirect.test(path);
    List<Redirect> redirects = REDIRECTS.stream()
      .filter(match)
      .toList();

    switch (redirects.size()) {
      case 0:
        chain.doFilter(request, response);
        break;
      case 1:
        response.sendRedirect(redirects.get(0).apply(request));
        break;
      default:
        throw new IllegalStateException(format("Multiple redirects have been found for '%s'", path));
    }
  }

  public static Redirect newSimpleRedirect(String from, String to) {
    return new Redirect() {
      @Override
      public boolean test(String path) {
        return from.equals(path);
      }

      @Override
      public String apply(HttpServletRequest request) {
        return format("%s%s", request.getContextPath(), to);
      }
    };
  }

  @Override

View on GitHub (pinned to 184c821202)