{"record":{"id":"62454bdd348de6d5","repo":"yudaocode/SpringBoot-Labs","slug":"error-62454b","errorCode":null,"errorMessage":"故意抛个错误","messagePattern":"故意抛个错误","errorType":"http","errorClass":"RuntimeException","httpStatus":500,"severity":"warning","filePath":"lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java","lineNumber":29,"sourceCode":"public class ThirdInterceptor implements HandlerInterceptor {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        logger.info(\"[preHandle][handler({})]\", handler);\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        logger.info(\"[postHandle][handler({})]\", handler);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n        throw new RuntimeException(\"故意抛个错误\"); // 故意抛出异常\n    }\n\n}\n","sourceCodeStart":11,"sourceCodeEnd":33,"githubUrl":"https://github.com/yudaocode/SpringBoot-Labs/blob/6c12efaed06d12907a0f40dd2ad1f7020aec8798/lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java#L11-L33","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Override\npublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {\n    logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n    throw new RuntimeException(\"故意抛个错误\");\n}\n\n// after\n@Override\npublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {\n    try {\n        logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n    } catch (Exception cleanupEx) {\n        logger.warn(\"[afterCompletion][cleanup failed]\", cleanupEx);\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Inside afterCompletion itself — the only correct place:\n@Override\npublic void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {\n    try {\n        // cleanup logic\n    } catch (Exception cleanupEx) {\n        logger.warn(\"afterCompletion cleanup failed\", cleanupEx);\n    }\n}","preventionTips":["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."],"tags":["spring-mvc","interceptor","after-completion","tutorial","deliberate-throw"],"backgroundTag":null,"analyzedSha":"6c12efaed06d12907a0f40dd2ad1f7020aec8798","analyzedAt":"2026-08-14T13:06:31.500Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}