Skip to content

Post Request with springboot4 Container hangs #1598

Closed

Description

@Vadym79

I have a simple example application which uses AWS Serverless Java Container for Spring Boot 4 to store and retrieve a product from DynamoDB. You can deploy it with AWS SAM.

It works, if I use my own handler implementation for the PostProductFunction (which creates a new product, see template.yaml) like this:

PostProductFunction:
Type: AWS::Serverless::Function
Properties:
Handler: software.amazonaws.example.product.handler.StreamLambdaHandler::handleRequest

If I switch the handler to use

Handler: com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler

and activate a required MAIN_CLASS environment variable in the Globals Function section of the SAM template like this:

Globals:
Function:
.....
Environment:
Variables:
MAIN_CLASS: software.amazonaws.Application
REGION: !Sub ${AWS::Region}
JAVA_TOOL_OPTIONS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
PRODUCT_TABLE_NAME: !Ref ProductsTable

The POST request sent from the API Gateway with Content-Type:application/json as a HTTP Header and JSON payload { "id": 1, "name": "Print 10x13", "price": 15 } as a HTTP body runs into the 30 second timeout of the API Gateway and Lambda function even if I clearly see in the Lambda logs, that the request is executed, which means that ProductController createProduct method is sucessfully finished within a few seconds (and the product is created in the DynamoDB). I don't have any control what's happening outside of the Controller method when using the SpringDelegatingLambdaContainerHandler as a handler.

If I switch from POST to PUT in the SAM template and ProductController the behavior is the same (it hangs with the com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler handler).

I don't have such a problem with the GET request (for example GetProductByID) which finishes successfully in time.

Please help me investigate why the problem arise? Maybe I'm doing something wrong.

ADDED : I found out how to make it work. It didn't work when the createProduct method of ProductController returned void and didn't have produces mime type. Now I changed both (returning String and defined text/plain as produces mime type) and it works with com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler handler as well. Still it worked with returning void and not having produces mime type for POST/PUT with AWS Serverless Java Spring Boot 3 Container and Spring Boot 3 (I cheked my working examples). The most important thing: it should work the same way regardless whether I define my own StreamHandler , which works fine with return void and without produces mime type for POST/PUT or re-use the existing com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler handler.
And, of course, I'd at least expect exception, instead of hanging and runnung into timeout. So please review the issue and fix it.

Activity

  1. kosmakdm commented on Mar 3, 2026

    @kosmakdm
    Contributor

    Hi! I have the same issue when I migrate to springboot4. I've done some investigation and here are my findings.

    Root Cause

    Both SpringDelegatingLambdaContainerHandler.handleRequest() and AwsSpringWebCustomRuntimeEventLoop.eventLoop() unconditionally call httpServletRequest.startAsync() before delegating to AwsSpringHttpProcessingUtils.processRequest(). The springboot3 module does NOT make this call.

    Calling startAsync() sets the request's dispatcher type to DispatcherType.ASYNC. After the servlet chain completes, there is a safety-net flush:

    if (!response.isCommitted() && request.getDispatcherType() != DispatcherType.ASYNC) {
        response.flushBuffer();
    }

    With dispatcher type ASYNC, this safety net is skipped. The response's flushBuffer() is the only place that calls writersCountDownLatch.countDown(). For controllers that return a body, the HttpMessageConverter flushes the output stream during serialization, so the latch counts down regardless. But for "void-returning controllers" no converter runs, no flush happens, and latch.await(15, TimeUnit.MINUTES) blocks until timeout.

    Why it works with springboot3: the springboot3 module never calls startAsync(), so the safety-net flush always fires.

    Why GET works but POST with void return doesn't: GET endpoints typically return a body, which triggers the flush through the message converter path.

    Fix: Remove the unconditional startAsync() calls from the springboot4 module. I've submitted a PR with this fix and a test: pr

  2. toinux commented on Mar 9, 2026

    @toinux

    Hello, another issue, I was trying oauth2 authentication with :

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security-oauth2-resource-server</artifactId>
            </dependency>

    the BearerTokenAuthenticationFilter did not work because it extends OncePerRequestFilter, and OncePerRequestFilter skips dispatch if DispatcherType is ASYNC :

    	private boolean skipDispatch(HttpServletRequest request) {
    		if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {
    			return true;
    		}
    		if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {
    			return true;
    		}
    		return false;
    	}
    
    // [...]
    
    	protected boolean isAsyncDispatch(HttpServletRequest request) {
    		return DispatcherType.ASYNC.equals(request.getDispatcherType());
    	}

    @kosmakdm I confirm that #1600 solves this issue too.

    Thanks !

  3. fraser148 commented on Mar 9, 2026

    @fraser148

    Also running into this, would love to see this merged? @tobixlea

  4. tobixlea commented on Mar 16, 2026

    @tobixlea
    Contributor

    Note: It may take up to a few hours for the artifact to be published and available on Maven Central

  5. tobixlea commented on Mar 16, 2026

    @tobixlea
    Contributor

    Keeping thread open in order to verify fix is working properly for users before closing

  6. toinux commented on Mar 17, 2026

    @toinux

    So far it works for me !

  7. tobixlea commented on Mar 20, 2026

    @tobixlea
    Contributor

    Closing as fix has been merged and no further issue has been brought forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions