๐ด The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
org.springframework.web.context.request.async.AsyncRequestTimeoutException: null
at org.springframework.web.context.request.async.TimeoutDeferredResultProcessingInterceptor.handleTimeout(TimeoutDeferredResultProcessingInterceptor.java:48)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$TimeoutRunnable.run(StandardServletAsyncWebRequest.java:319)โก Quick Fix Works 80% of the time
Increase the spring.mvc.async.request-timeout property in application.properties.
# application.properties
spring.mvc.async.request-timeout=60000 # 60 seconds๐ง Why this Happens
Tap to expand the deep technical explanation
Your controller returned an asynchronous response (like `StreamingResponseBody`, `SseEmitter`, or `DeferredResult`). The client kept the connection open waiting for data, but the data didn't arrive within the configured timeout window (default is 30 seconds in Tomcat). Spring aborted the connection.
The HITEC City Parking Spot Analogy:
It's like ordering food at a drive-thru and being told to pull forward and wait. If you sit there for 30 minutes, the manager (Spring) will eventually tell you to drive away because you're holding up the line.
๐ How to Reproduce Confirm this is your error
Create a controller returning `SseEmitter`. Do not send any events. The client will hold the connection. After 30 seconds, the server will throw this exception.
๐ ๏ธ Solutions (5 Ways to Fix)
Increase the global async timeout
๐ Use this if your async tasks legitimately take longer than 30 seconds.
Configure Spring MVC to wait longer before aborting async requests.
# application.properties
# Set to 60 seconds (in milliseconds)
spring.mvc.async.request-timeout=60000Set timeout on the emitter object
๐ Use this if you want per-request timeout control instead of global.
You can set the timeout directly on the SseEmitter or DeferredResult object.
@GetMapping("/events")
public SseEmitter handle() {
// Set timeout to 2 minutes for this specific request
SseEmitter emitter = new SseEmitter(120000L);
// ...
return emitter;
}Fix long-running blocking tasks
๐ Use this if the async task is stuck in an infinite loop or DB lock.
If your task never completes, increasing the timeout just delays the error. Debug the thread to find why it's hanging.
// Use logging to trace where the thread is stuck
log.info("Starting async task...");
// ... long running code
log.info("Async task finished.");Use WebFlux instead of MVC (Reactive)
๐ Use this if you are building a heavily streaming/real-time app.
Spring WebFlux handles backpressure and long-lived connections much better than Spring MVC's async support.
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamEvents() {
return Flux.interval(Duration.ofSeconds(1)).map(seq -> "Event " + seq);
}Handle the timeout gracefully on the client
๐ Use this if timeouts are expected (e.g., client disconnects).
On the server side, implement the `onTimeout` callback to clean up resources.
SseEmitter emitter = new SseEmitter();
emitter.onTimeout(() -> {
log.warn("Client timed out");
emitter.complete();
});๐ Version Notes
Default MVC async timeout is 30s (Tomcat).
Default is 30s, but better integration with virtual threads.
๐ก๏ธ How to Prevent This Next Time
For real-time streaming (like chat or live feeds), prefer Spring WebFlux over Spring MVC. It is designed to handle thousands of long-lived connections without exhausting thread pools.