{"record":{"id":"89d9f221c7f34645","repo":"spring-projects/spring-security","slug":"unable-to-handle-the-spring-security-exception-bec","errorCode":null,"errorMessage":"Unable to handle the Spring Security Exception because the response is already committed.","messagePattern":"Unable to handle the Spring Security Exception because the response is already committed\\.","errorType":"exception","errorClass":"ServletException","httpStatus":null,"severity":"error","filePath":"web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java","lineNumber":144,"sourceCode":"\t\t\tchain.doFilter(request, response);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Try to extract a SpringSecurityException from the stacktrace\n\t\t\tThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\t\tRuntimeException securityException = (AuthenticationException) this.throwableAnalyzer\n\t\t\t\t.getFirstThrowableOfType(AuthenticationException.class, causeChain);\n\t\t\tif (securityException == null) {\n\t\t\t\tsecurityException = (AccessDeniedException) this.throwableAnalyzer\n\t\t\t\t\t.getFirstThrowableOfType(AccessDeniedException.class, causeChain);\n\t\t\t}\n\t\t\tif (securityException == null) {\n\t\t\t\trethrow(ex);\n\t\t\t}\n\t\t\tif (response.isCommitted()) {\n\t\t\t\tthrow new ServletException(\"Unable to handle the Spring Security Exception \"\n\t\t\t\t\t\t+ \"because the response is already committed.\", ex);\n\t\t\t}\n\t\t\thandleSpringSecurityException(request, response, chain, securityException);\n\t\t}\n\t}\n\n\tprivate void rethrow(Exception ex) throws ServletException {\n\t\t// Rethrow ServletExceptions and RuntimeExceptions as-is\n\t\tif (ex instanceof ServletException) {\n\t\t\tthrow (ServletException) ex;\n\t\t}\n\t\tif (ex instanceof RuntimeException) {\n\t\t\tthrow (RuntimeException) ex;\n\t\t}\n\t\t// Wrap other Exceptions. This shouldn't actually happen\n\t\t// as we've already covered all the possibilities for doFilter\n\t\tthrow new RuntimeException(ex);\n\t}","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java#L126-L162","documentation":"ExceptionTranslationFilter catches AuthenticationException/AccessDeniedException so it can start authentication (login redirect) or handle access denial. If the HTTP response is already committed (headers/body flushed to the client), it can no longer send a redirect or error response, so it rethrows the exception wrapped in a ServletException. This prevents silently swallowing a security exception it could not handle.","triggerScenarios":"An AccessDeniedException or AuthenticationException thrown downstream after the response was committed — e.g. streamed content already written, response.flushBuffer() called, or a security check occurring during body streaming inside chain.doFilter().","commonSituations":"File download/streaming endpoints where authorization is checked mid-stream; large responses that flush before a filter later in the chain throws; JSP pages that write output before a security tag throws.","solutions":["Move authorization checks earlier — before any response content is written (e.g. via authorizeHttpRequests rules instead of in-body checks)","Check response.isCommitted() before writing/flushing early in your handler","If streaming is unavoidable, buffer until authorization completes, or use AuthorizationFilter at the filter level","Catch ServletException downstream only to log; the original security exception is the cause"],"exampleFix":"// before\nOutputStream out = response.getOutputStream();\nstream(file, out); // commits response; later access check throws\n// after\nif (accessDenied) { throw new AccessDeniedException(...); } // before committing\nOutputStream out = response.getOutputStream();\nstream(file, out);","handlingStrategy":"try-catch","validationCode":"if (response.isCommitted()) {\n    log.warn(\"Response committed; skip triggering security handling\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    chain.doFilter(request, response);\n} catch (ServletException e) {\n    if (e.getCause() instanceof AuthenticationException || e.getCause() instanceof AccessDeniedException) {\n        log.warn(\"Security exception after commit: {}\", e.getCause().getMessage());\n    } else { throw e; }\n}","preventionTips":["Never write/flush response body before authorization completes","Enforce access control in authorizeHttpRequests, not mid-handler","Check response.isCommitted() before streaming operations"],"tags":["spring-security","filter-chain","response-committed","servlet"],"backgroundTag":"invalid-state-transition","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}