๐Ÿ”ด The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOGorg.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)

Solution 1โœ“ Most common cause

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=60000
Solution 2

Set 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; }
Solution 3

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.");
Solution 4

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); }
Solution 5

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

Spring Boot 2.x

Default MVC async timeout is 30s (Tomcat).

Spring Boot 3.x

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.

Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase