spring-projects/spring-security · error · UnreachableFilterChainException
The FilterChainProxy contains two filter chains using the ma
Error message
The FilterChainProxy contains two filter chains using the matcher {defaultChain.getRequestMatcher()} What it means
checkForDuplicateMatchers detects two SecurityFilterChain instances whose RequestMatchers are equal; the second chain is unreachable because the first always matches, so UnreachableFilterChainException is thrown with the duplicated matcher in the message.
Source
Thrown at config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidator.java:79
+ "] has already been configured, which means that this filter chain [" + chain
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
throw new UnreachableFilterChainException(message, anyRequestFilterChain, chain);
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
anyRequestFilterChain = defaultChain;
}
}
}
}
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
DefaultSecurityFilterChain filterChain = null;
for (SecurityFilterChain chain : chains) {
if (filterChain != null) {
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
throw new UnreachableFilterChainException(
"The FilterChainProxy contains two filter chains using the" + " matcher "
+ defaultChain.getRequestMatcher(),
filterChain, defaultChain);
}
}
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
filterChain = defaultChain;
}
}
}
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
Filter authorizationFilter = null;
Filter filterSecurityInterceptor = null;
for (SecurityFilterChain chain : chains) {
for (Filter filter : chain.getFilters()) {
if (filter instanceof AuthorizationFilter) {View on GitHub (pinned to 96852e8860)
Solutions
- Give each chain a distinct securityMatcher pattern
- Remove the duplicate chain bean if it is not needed
- Differentiate the chains with @Order plus disjoint matchers so only one can match a given request
Example fix
// before
SecurityFilterChain api(HttpSecurity http) { http.securityMatcher("/api/**"); ... }
SecurityFilterChain apiCopy(HttpSecurity http) { http.securityMatcher("/api/**"); ... }
// after
SecurityFilterChain api(HttpSecurity http) { http.securityMatcher("/api/**"); ... }
SecurityFilterChain other(HttpSecurity http) { http.securityMatcher("/other/**"); ... } Defensive patterns
Strategy: validation
Validate before calling
Set<RequestMatcher> seen = new HashSet<>();
for (SecurityFilterChain c : chains) {
if (c instanceof DefaultSecurityFilterChain d && !seen.add(d.getRequestMatcher())) {
throw new IllegalStateException("Duplicate matcher: " + d.getRequestMatcher());
}
} Try / catch
try {
webSecurity.build();
} catch (UnreachableFilterChainException e) {
if (e.getMessage().contains("two filter chains using the matcher")) {
logger.error("Duplicate securityMatcher across chains: " + e.getMessage());
}
throw e;
} Prevention
- Keep a single source of truth for securityMatcher patterns per chain
- When copying chains for new routes, change the matcher immediately
- Use disjoint, route-specific matchers and reserve anyRequest() for exactly one final chain
When it happens
Trigger: Publishing multiple SecurityFilterChain beans with identical securityMatcher patterns (e.g. two chains both matching /api/**); duplicated conditional configurations that both end up active; copying a chain without changing its matcher.
Common situations: Multiple WebSecurityConfigurerAdapter/SecurityFilterChain beans with copy-pasted matchers; profile-based configs where conditions overlap; generated configurations registering the same matcher twice.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- The Filter class {registeredFilter.getName()} does not have
- The Filter class {filter.getClass().getName()} does not have
- A filter chain that matches any request [{anyRequestFilterCh
- Possible error: Filters at position <i> and <j> are both ins
- Cannot apply {configurer} to already built object
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/1b8e89bbb8476276.
Report an issue: GitHub.