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

  1. Change the pattern attribute (or RequestMatcher) of the duplicate chain so each chain matches a distinct request set
  2. Delete the redundant duplicate chain if it is not needed
  3. Verify every <http> element in multi-http configurations has a unique, non-overlapping pattern
  4. 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

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


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)