Affects: 6.1.5

Hello. I noticed that when using the RFC7807 error handler, the AsyncRequestTimeoutException is not handled correctly.

The org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler class handles this exception:

else if (ex instanceof AsyncRequestTimeoutException subEx) {
    return handleAsyncRequestTimeoutException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
}

however, the getBody() method in the exception itself always returns a new object:

@Override
public ProblemDetail getBody() {
    return ProblemDetail.forStatus(getStatusCode());
}

Because of this, populating the fields in the org.springframework.web.ErrorResponse#updateAndGetBody method (that called from org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleExceptionInternal) has no effect.

Reproduction: - enable a rfc7807 handler: spring.mvc.problemdetails.enabled: true. - add custom messages to default message source

# AsyncRequestTimeoutException
problemDetail.type.org.springframework.web.context.request.async.AsyncRequestTimeoutException=about:blank
problemDetail.title.org.springframework.web.context.request.async.AsyncRequestTimeoutException=request timeout
problemDetail.org.springframework.web.context.request.async.AsyncRequestTimeoutException=timeout reached
  • create controller and throw exception like this:
   @GetMapping("/springError/AsyncRequestTimeoutException")
    public TestResponseDto asyncRequestTimeoutException() {
        throw new AsyncRequestTimeoutException();
    }

we expect to see a response like this:

{
    "type": "about:blank",
    "title": "request timeout",
    "status": 500,
    "detail": "timeout reached",
    "instance": "/api/test/springError/AsyncRequestTimeoutException"
}

but in fact the response is:

{
    "type": "about:blank",
    "title": "Service Unavailable",
    "status": 503,
    "instance": "/api/test/springError/AsyncRequestTimeoutException"
}

Suggestion

I guess it can be fixed in AsyncRequestTimeoutException like that:

    private final ProblemDetail body = ProblemDetail.forStatus(getStatusCode());
        ...
    @Override
    public ProblemDetail getBody() {
        return this.body;
    }

Workaround as a workaround, i overridden method ru.wbbank.error.handler.starter.webmvc.controller.WBProblemDetailsExceptionHandler#handleAsyncRequestTimeoutException.

We create problemDetail manually, not by AsyncRequestTimeoutException:

    @Override
    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(
        AsyncRequestTimeoutException ex,
        HttpHeaders headers,
        HttpStatusCode status,
        WebRequest request
    ) {
        var problemDetail = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        var errResp = ErrorResponse.builder(ex, problemDetail).build();
        problemDetail = errResp.updateAndGetBody(getMessageSource(), LocaleContextHolder.getLocale());
        return this.handleExceptionInternal(ex, problemDetail, errResp.getHeaders(), errResp.getStatusCode(), request);
    }

Comment From: facewise

@rstoyanchev Can I pick up this issue?

Comment From: rstoyanchev

@facewise, apologies for not seeing your comment. I've also checked other implementations, and updated the Javadoc so the change was a little wider.