{"record":{"id":"1b8e89bbb8476276","repo":"spring-projects/spring-security","slug":"the-filterchainproxy-contains-two-filter-chains-us","errorCode":null,"errorMessage":"The FilterChainProxy contains two filter chains using the matcher {defaultChain.getRequestMatcher()}","messagePattern":"The FilterChainProxy contains two filter chains using the matcher (.+?)","errorType":"exception","errorClass":"UnreachableFilterChainException","httpStatus":null,"severity":"error","filePath":"config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidator.java","lineNumber":79,"sourceCode":"\t\t\t\t\t\t+ \"] has already been configured, which means that this filter chain [\" + chain\n\t\t\t\t\t\t+ \"] 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.\";\n\t\t\t\tthrow new UnreachableFilterChainException(message, anyRequestFilterChain, chain);\n\t\t\t}\n\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\tif (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {\n\t\t\t\t\tanyRequestFilterChain = defaultChain;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {\n\t\tDefaultSecurityFilterChain filterChain = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tif (filterChain != null) {\n\t\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\t\tif (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {\n\t\t\t\t\t\tthrow new UnreachableFilterChainException(\n\t\t\t\t\t\t\t\t\"The FilterChainProxy contains two filter chains using the\" + \" matcher \"\n\t\t\t\t\t\t\t\t\t\t+ defaultChain.getRequestMatcher(),\n\t\t\t\t\t\t\t\tfilterChain, defaultChain);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\tfilterChain = defaultChain;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkAuthorizationFilters(List<SecurityFilterChain> chains) {\n\t\tFilter authorizationFilter = null;\n\t\tFilter filterSecurityInterceptor = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tfor (Filter filter : chain.getFilters()) {\n\t\t\t\tif (filter instanceof AuthorizationFilter) {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidator.java#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"Multiple WebSecurityConfigurerAdapter/SecurityFilterChain beans with copy-pasted matchers; profile-based configs where conditions overlap; generated configurations registering the same matcher twice.","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"],"exampleFix":"// before\nSecurityFilterChain api(HttpSecurity http) { http.securityMatcher(\"/api/**\"); ... }\nSecurityFilterChain apiCopy(HttpSecurity http) { http.securityMatcher(\"/api/**\"); ... }\n// after\nSecurityFilterChain api(HttpSecurity http) { http.securityMatcher(\"/api/**\"); ... }\nSecurityFilterChain other(HttpSecurity http) { http.securityMatcher(\"/other/**\"); ... }","handlingStrategy":"validation","validationCode":"Set<RequestMatcher> seen = new HashSet<>();\nfor (SecurityFilterChain c : chains) {\n    if (c instanceof DefaultSecurityFilterChain d && !seen.add(d.getRequestMatcher())) {\n        throw new IllegalStateException(\"Duplicate matcher: \" + d.getRequestMatcher());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    webSecurity.build();\n} catch (UnreachableFilterChainException e) {\n    if (e.getMessage().contains(\"two filter chains using the matcher\")) {\n        logger.error(\"Duplicate securityMatcher across chains: \" + e.getMessage());\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["spring-security","filter-chain","duplicate-matchers","configuration"],"backgroundTag":"conflicting-config-options","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}