asyncComplete() is called in the "finally" section on both success and failure of the background request processing. Sometimes when there is an IO error on the background thread I get the following error: java.lang.IllegalStateException: Calling [asyncComplete()] is not valid for a request with Async state [MUST_ERROR] java.lang.IllegalStateException: Calling [asyncComplete()] is not valid for a request with Async state [MUST_ERROR] at org.apache.coyote.AsyncStateMachine.doComplete(AsyncStateMachine.java:332) at org.apache.coyote.AsyncStateMachine.asyncComplete(AsyncStateMachine.java:316) at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:496) at org.apache.coyote.Request.action(Request.java:430) at org.apache.catalina.core.AsyncContextImpl.complete(AsyncContextImpl.java:92)
Thanks for the report. It has triggered some useful review and clean-up in the Async code (and I still have a little more do clean-up). Fixed in: - master for 9.0.28 onwards - 8.5.x for 8.5.48 onwards - 7.0.x for 7.0.98 onwards I have spent several days reviewing the error handling of the async code and writing some additional test cases to improve the coverage. You probably won't see the exact error above any more but it is possible that you will continue to see errors with your current error handling. When an I/O error occurs on a non-Tomcat thread (i.e. on a thread the app has started) Tomcat detects the error and fires an internal error event so that AsyncListener.onError() can be notified. The Servlet spec defines various behaviours for this listener. Essentially, the expectation is that error handling occurs during onError(). If complete() or dispatch() are not called during onError(), Tomcat is required to call complete(). Therefore, if you handle the error on the original non-Tomcat thread you are likely to see IllegalStateExceptions as the error handling on that thread conflicts with spec mandated the error handling Tomcat is performing. While the specification is not specific (or if it is, I have missed it) my reading of the spec is that it strongly implies error handling must be in AsyncListener.onError().