spring-projects/spring-security · error · UnreachableFilterChainException
The FilterChainProxy contains two filter chains using the…
Error message
The FilterChainProxy contains two filter chains using the matcher {requestMatcher}. If you are using multiple <http> namespace elements, you must use a 'pattern' attribute to define the request patterns to which they apply. What it means
DefaultFilterChainValidator.checkForDuplicateMatchers walks adjacent filter chains and throws UnreachableFilterChainException when two consecutive DefaultSecurityFilterChain instances use an equal RequestMatcher, because the second chain can never be reached. The message directs users of multiple <http> elements to differentiate them with distinct pattern attributes.
Solutions
- Change the pattern attribute (or RequestMatcher) of the duplicate chain so each chain matches a distinct request set
- Delete the redundant duplicate chain if it is not needed
- Verify every <http> element in multi-http configurations has a unique, non-overlapping pattern
- Use distinct request matchers (e.g. different AntPathRequestMatcher paths or method+path combinations) in programmatic setups
Example fix
// before <http pattern="/api/**" security="none"/> <http pattern="/api/**" ...>...</http> // after <http pattern="/api/public/**" security="none"/> <http pattern="/api/**" ...>...</http>
Defensive patterns
Strategy: validation
Validate before calling
java.util.Set<RequestMatcher> seen = new java.util.HashSet<>();
for (SecurityFilterChain c : chains) {
if (!seen.add(((DefaultSecurityFilterChain) c).getRequestMatcher())) throw new IllegalStateException("Duplicate matcher: " + c);
} Try / catch
try {
filterChainProxy.afterPropertiesSet();
} catch (UnreachableFilterChainException e) {
logger.error("Duplicate chain matcher: {}", e.getMessage());
} Prevention
- Give every <http> element a unique pattern attribute
- Grep generated/template XML for repeated pattern values
- Differentiate chains by path and HTTP method matchers
When it happens
Trigger: Two adjacent SecurityFilterChains return equal RequestMatchers from getRequestMatcher() — e.g. two <http> elements both with pattern="/foo/**" (or both defaulting to /**), or two programmatically registered chains built from the same matcher instance/equal matcher.
Common situations: Copy-pasting an <http> block and forgetting to change its pattern attribute; duplicate chain registrations during configuration refactoring; generating <http> entries from a loop/template with a repeated pattern value.
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
- A universal match pattern ('/**') is defined before other…
- A filter chain that matches any request
- Anonymous access to the login page doesn't appear to be…
- Duplicate URL defined
- It is not recommended to use authorizeRequests or…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/50178116e8c0f426.
Report an issue: GitHub.
Appendix: source
Thrown at config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java:104
while (chains.hasNext()) {
if (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {
if (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {
throw new UnreachableFilterChainException("A universal match pattern ('/**') is defined "
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration",
securityFilterChain, chains.next());
}
}
}
}
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()
+ ". If you are using multiple <http> namespace "
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.",
defaultChain, chain);
}
}
}
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
filterChain = defaultChain;
}
}
}
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
Filter authorizationFilter = null;
Filter filterSecurityInterceptor = null;
for (SecurityFilterChain chain : chains) {View on GitHub (pinned to 96852e8860)