Skip to content

NullPointerException with GraphQlSseHandler in case of async timeouts #1067

Closed
@jjavery

Description

@jjavery

I'm using graphql-sse to connect to a basic Spring GraphQL @SubscriptionMapping that returns a Flux<>. I'm seeing a Null Pointer Exception in the log every 30 seconds.

 reactor.core.publisher.Operators         : Operator called default onErrorDropped

java.lang.NullPointerException: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null
        at org.apache.catalina.connector.CoyoteOutputStream.checkNonBlockingWrite(CoyoteOutputStream.java:154) ~[tomcat-embed-core-10.1.30.jar:10.1.30]

Here's a Spring Initializr project demonstrating the problem:

https://github.com/jjavery/graphql-subscription-server-sent-events-npe

To reproduce: bootRun and open a browser to http://localhost:8080/ . Every 30 seconds you'll see the NPE in the log. Also there are unexpected warnings: "Ignoring exception, response committed already: org.springframework.web.context.request.async.AsyncRequestTimeoutException"

Includes the bug-fixed 1.3.3 version of GraphQlSseHandler.java copied from this commit:
d3cd569

The 30 seconds comes from Tomcat's asyncTimeout. See TomcatConfiguration.java to change the asyncTimeout.

Log:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

2024-10-15T14:09:03.879-05:00  INFO 33774 --- [  restartedMain] com.example.demo.DemoApplication         : Starting DemoApplication using Java 17.0.12 with PID 33774 (/Users/jamie/Downloads/demo/bin/main started by jamie in /Users/jamie/Downloads/demo)
2024-10-15T14:09:03.881-05:00  INFO 33774 --- [  restartedMain] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2024-10-15T14:09:03.909-05:00  INFO 33774 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2024-10-15T14:09:03.909-05:00  INFO 33774 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2024-10-15T14:09:04.376-05:00  INFO 33774 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-10-15T14:09:04.384-05:00  INFO 33774 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-10-15T14:09:04.384-05:00  INFO 33774 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.30]
2024-10-15T14:09:04.404-05:00  INFO 33774 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-10-15T14:09:04.405-05:00  INFO 33774 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 495 ms
2024-10-15T14:09:04.557-05:00  INFO 33774 --- [  restartedMain] efaultSchemaResourceGraphQlSourceBuilder : Loaded 1 resource(s) in the GraphQL schema.
2024-10-15T14:09:04.650-05:00  INFO 33774 --- [  restartedMain] o.s.b.a.g.GraphQlAutoConfiguration       : GraphQL schema inspection:
        Unmapped fields: {}
        Unmapped registrations: {}
        Unmapped arguments: {}
        Skipped types: []
2024-10-15T14:09:04.681-05:00  INFO 33774 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2024-10-15T14:09:04.725-05:00  INFO 33774 --- [  restartedMain] s.b.a.g.s.GraphQlWebMvcAutoConfiguration : GraphQL endpoint HTTP POST /graphql
2024-10-15T14:09:04.779-05:00  WARN 33774 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : Unable to start LiveReload server
2024-10-15T14:09:04.795-05:00  INFO 33774 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-10-15T14:09:04.800-05:00  INFO 33774 --- [  restartedMain] com.example.demo.DemoApplication         : Started DemoApplication in 1.118 seconds (process running for 1.354)
2024-10-15T14:09:09.366-05:00  INFO 33774 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-10-15T14:09:09.366-05:00  INFO 33774 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-10-15T14:09:09.367-05:00  INFO 33774 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2024-10-15T14:09:39.809-05:00  WARN 33774 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Ignoring exception, response committed already: org.springframework.web.context.request.async.AsyncRequestTimeoutException
2024-10-15T14:09:39.810-05:00  WARN 33774 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.context.request.async.AsyncRequestTimeoutException]
2024-10-15T14:09:40.481-05:00 ERROR 33774 --- [     parallel-1] reactor.core.publisher.Operators         : Operator called default onErrorDropped

java.lang.NullPointerException: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null
        at org.apache.catalina.connector.CoyoteOutputStream.checkNonBlockingWrite(CoyoteOutputStream.java:154) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:101) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.write(StandardServletAsyncWebRequest.java:398) ~[spring-web-6.1.13.jar:6.1.13]
        at java.base/java.io.OutputStream.write(OutputStream.java:127) ~[na:na]
        at org.springframework.web.servlet.function.SseServerResponse$DefaultSseBuilder.send(SseServerResponse.java:143) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.function.SseServerResponse$DefaultSseBuilder.writeString(SseServerResponse.java:206) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.function.SseServerResponse$DefaultSseBuilder.data(SseServerResponse.java:194) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at com.example.demo.GraphQlSseHandler133$SseSubscriber.hookOnComplete(GraphQlSseHandler133.java:135) ~[main/:na]
        at com.example.demo.GraphQlSseHandler133$SseSubscriber.hookOnError(GraphQlSseHandler133.java:129) ~[main/:na]
        at reactor.core.publisher.BaseSubscriber.onError(BaseSubscriber.java:180) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:163) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:251) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.6.10.jar:3.6.10]
        at graphql.execution.reactive.CompletionStageMappingPublisher$CompletionStageSubscriber.lambda$whenNextFinished$0(CompletionStageMappingPublisher.java:100) ~[graphql-java-22.1.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:887) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2325) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:144) ~[na:na]
        at graphql.execution.reactive.CompletionStageMappingPublisher$CompletionStageSubscriber.onNext(CompletionStageMappingPublisher.java:88) ~[graphql-java-22.1.jar:na]
        at reactor.core.publisher.StrictSubscriber.onNext(StrictSubscriber.java:89) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.publisher.FluxInterval$IntervalRunnable.run(FluxInterval.java:124) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.scheduler.PeriodicWorkerTask.call(PeriodicWorkerTask.java:59) ~[reactor-core-3.6.10.jar:3.6.10]
        at reactor.core.scheduler.PeriodicWorkerTask.run(PeriodicWorkerTask.java:73) ~[reactor-core-3.6.10.jar:3.6.10]
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]
        at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]
        Suppressed: java.lang.NullPointerException: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null
                at org.apache.catalina.connector.CoyoteOutputStream.checkNonBlockingWrite(CoyoteOutputStream.java:154) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
                at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:101) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
                at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.write(StandardServletAsyncWebRequest.java:398) ~[spring-web-6.1.13.jar:6.1.13]
                at java.base/java.io.OutputStream.write(OutputStream.java:127) ~[na:na]
                at org.springframework.web.servlet.function.SseServerResponse$DefaultSseBuilder.writeObject(SseServerResponse.java:213) ~[spring-webmvc-6.1.13.jar:6.1.13]
                at org.springframework.web.servlet.function.SseServerResponse$DefaultSseBuilder.data(SseServerResponse.java:197) ~[spring-webmvc-6.1.13.jar:6.1.13]
                at com.example.demo.GraphQlSseHandler133$SseSubscriber.writeResult(GraphQlSseHandler133.java:112) ~[main/:na]
                at com.example.demo.GraphQlSseHandler133$SseSubscriber.hookOnNext(GraphQlSseHandler133.java:106) ~[main/:na]
                at com.example.demo.GraphQlSseHandler133$SseSubscriber.hookOnNext(GraphQlSseHandler133.java:1) ~[main/:na]
                at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:160) ~[reactor-core-3.6.10.jar:3.6.10]
                ... 22 common frames omitted

Metadata

Metadata

Assignees

Labels

in: webIssues related to web handlingtype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions