yudaocode/SpringBoot-Labs · warning · RuntimeException
故意抛个错误
Error message
故意抛个错误
What it means
A RuntimeException('故意抛个错误') thrown inside ThirdInterceptor.afterCompletion, i.e. after the handler finished and the view/response processing completed. Spring MVC guarantees afterCompletion runs even on exception, but any exception thrown from it arrives after the response is essentially done: it cannot change the response body and is only logged (typically at WARN by DispatcherServlet's handler-interceptor cleanup). This lab endpoint exists to demonstrate that interceptor afterCompletion failures are mostly invisible to the client.
Source
Thrown at lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java:29
public class ThirdInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("[preHandle][handler({})]", handler);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("[postHandle][handler({})]", handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("[afterCompletion][handler({})]", handler, ex);
throw new RuntimeException("故意抛个错误"); // 故意抛出异常
}
}
View on GitHub (pinned to 6c12efaed0)
Solutions
- Never let afterCompletion throw: wrap its body in try-catch and only log, since the response is already committed and you cannot inform the client.
- Delete this deliberate throw (comment '故意抛出异常') once the demo behavior is verified.
- If cleanup can fail, make it idempotent and guard each resource individually so one failure does not skip the rest.
- Check logs (not the HTTP response) when verifying this demo: the client still receives the successful response.
Example fix
// before
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
logger.info("[afterCompletion][handler({})]", handler, ex);
throw new RuntimeException("故意抛个错误");
}
// after
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
logger.info("[afterCompletion][handler({})]", handler, ex);
} catch (Exception cleanupEx) {
logger.warn("[afterCompletion][cleanup failed]", cleanupEx);
}
} Defensive patterns
Strategy: try-catch
Try / catch
// Inside afterCompletion itself — the only correct place:
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
try {
// cleanup logic
} catch (Exception cleanupEx) {
logger.warn("afterCompletion cleanup failed", cleanupEx);
}
} Prevention
- Never throw from afterCompletion — the response is committed and the client cannot be informed.
- Make cleanup idempotent so a retry after partial cleanup is safe.
- Watch logs at WARN from DispatcherServlet for interceptor exceptions; they are invisible to clients.
When it happens
Trigger: Any request that traverses a HandlerMapping this interceptor is registered on, once the interceptor's afterCompletion is invoked — i.e., after every handled request, success or failure. The throw is unconditional in this demo.
Common situations: Developers put cleanup (resource release, metric recording) in afterCompletion and that cleanup itself throws (NPE on a null resource, closed connection). The client sees a normal 200 while the log fills with interceptor exceptions; in older Spring Boot versions, repeated afterCompletion exceptions can mask the original handler exception in logs.
Related errors
AI-assisted analysis of yudaocode/SpringBoot-Labs@6c12efaed0 (2026-08-14).
Data as JSON: /api/errors/62454bdd348de6d5.
Report an issue: GitHub.