{"record":{"id":"ccffc250f13f014e","repo":"spring-projects/spring-ai","slug":"failed-to-complete-sse-builder-e-getmessage","errorCode":null,"errorMessage":"Failed to complete SSE builder: + e.getMessage()","messagePattern":"Failed to complete SSE builder: \\+ e\\.getMessage\\(\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/transport/mcp-spring-webmvc/src/main/java/org/springframework/ai/mcp/server/webmvc/transport/WebMvcSseServerTransportProvider.java","lineNumber":529,"sourceCode":"\t\t@Override\n\t\tpublic <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {\n\t\t\treturn jsonMapper.convertValue(data, typeRef);\n\t\t}\n\n\t\t/**\n\t\t * Initiates a graceful shutdown of the transport.\n\t\t * @return A Mono that completes when the shutdown is complete\n\t\t */\n\t\t@Override\n\t\tpublic Mono<Void> closeGracefully() {\n\t\t\treturn Mono.fromRunnable(() -> {\n\t\t\t\tthis.sseBuilderLock.lock();\n\t\t\t\ttry {\n\t\t\t\t\tthis.sseBuilder.complete();\n\t\t\t\t}\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\t\tlogger.warn(\"Failed to complete SSE builder: \" + e.getMessage());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.sseBuilderLock.unlock();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Closes the transport immediately.\n\t\t */\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tthis.sseBuilderLock.lock();\n\t\t\ttry {\n\t\t\t\tthis.sseBuilder.complete();\n\t\t\t}\n\t\t\tcatch (Exception e) {","sourceCodeStart":511,"sourceCodeEnd":547,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/transport/mcp-spring-webmvc/src/main/java/org/springframework/ai/mcp/server/webmvc/transport/WebMvcSseServerTransportProvider.java#L511-L547","documentation":"A warning logged in WebMvcSseServerTransportProvider.closeGracefully when calling SseEmitter.complete() on the client's emitter throws (typically because the underlying connection is already closed, timed out, or the client disconnected). The lock is still released and the session continues closing; this warning only signals the SSE channel could not be completed cleanly.","triggerScenarios":"Client disconnected before server close; SSE emitter already completed or timed out (IllegalStateException); servlet container already invalidated the response; race between async timeout and graceful shutdown.","commonSituations":"Shutting down the MCP server while clients are connected; clients with flaky networks dropping mid-session; long-running sessions hitting servlet async timeouts; duplicate close calls on the same session.","solutions":["Treat the warning as benign if clients are already disconnected — verify sessions were cleaned up","Ensure closeGracefully is called once per session and coordinate with client disconnect callbacks","Adjust servlet async/SSE timeout settings to exceed expected session lifetimes","Catch and log client-disconnect conditions separately during shutdown to reduce noise"],"exampleFix":"// before\n// shutdown hooks calling provider.closeGracefully() while clients still connected -> warnings\n// after\n// notify sessions / wait for client disconnects, then:\nsession.closeGracefully()\n    .doOnError(e -> log.debug(\"SSE already closed for session\", e))\n    .block(Duration.ofSeconds(5));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"session.closeGracefully()\n    .doOnError(e -> log.debug(\"SSE close skipped: {}\", e.getMessage()))\n    .onErrorComplete()\n    .block(Duration.ofSeconds(5));","preventionTips":["Expect and ignore close errors on already-disconnected clients","Tune servlet async/SSE timeouts above session lifetime","Call closeGracefully only once per session","Coordinate shutdown with client disconnect callbacks"],"tags":["mcp","sse","shutdown","webmvc"],"backgroundTag":"broken-pipe","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}