spring-projects/spring-security · error · ServletException
Unable to handle the Spring Security Exception because the…
Error message
Unable to handle the Spring Security Exception because the response is already committed.
What it means
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.
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
Example fix
// before
OutputStream out = response.getOutputStream();
stream(file, out); // commits response; later access check throws
// after
if (accessDenied) { throw new AccessDeniedException(...); } // before committing
OutputStream out = response.getOutputStream();
stream(file, out); Defensive patterns
Strategy: try-catch
Validate before calling
if (response.isCommitted()) {
log.warn("Response committed; skip triggering security handling");
} Try / catch
try {
chain.doFilter(request, response);
} catch (ServletException e) {
if (e.getCause() instanceof AuthenticationException || e.getCause() instanceof AccessDeniedException) {
log.warn("Security exception after commit: {}", e.getCause().getMessage());
} else { throw e; }
} Prevention
- Never write/flush response body before authorization completes
- Enforce access control in authorizeHttpRequests, not mid-handler
- Check response.isCommitted() before streaming operations
When it happens
Trigger: 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().
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- A filter chain that matches any request
- A universal match pattern ('/**') is defined before other…
- Anonymous access to the login page doesn't appear to be…
- It is not recommended to use authorizeRequests or…
- Possible error: Filters at position
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/89d9f221c7f34645.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java:144
chain.doFilter(request, response);
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// Try to extract a SpringSecurityException from the stacktrace
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (securityException == null) {
securityException = (AccessDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (securityException == null) {
rethrow(ex);
}
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception "
+ "because the response is already committed.", ex);
}
handleSpringSecurityException(request, response, chain, securityException);
}
}
private void rethrow(Exception ex) throws ServletException {
// Rethrow ServletExceptions and RuntimeExceptions as-is
if (ex instanceof ServletException) {
throw (ServletException) ex;
}
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
// Wrap other Exceptions. This shouldn't actually happen
// as we've already covered all the possibilities for doFilter
throw new RuntimeException(ex);
}View on GitHub (pinned to 96852e8860)