{"record":{"id":"94178a56cd15ce93","repo":"spring-projects/spring-ai","slug":"transport-has-no-request-handler-registered-remem","errorCode":null,"errorMessage":"Transport has no request handler registered. Remember to call connect!","messagePattern":"Transport has no request handler registered\\. Remember to call connect!","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/transport/mcp-spring-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/transport/WebClientStreamableHttpTransport.java","lineNumber":231,"sourceCode":"\n\t@Override\n\tpublic Mono<Void> closeGracefully() {\n\t\treturn Mono.defer(() -> {\n\t\t\tlogger.debug(\"Graceful close triggered\");\n\t\t\tMcpTransportSession<Disposable> currentSession = this.activeSession\n\t\t\t\t.getAndSet(ClosedMcpTransportSession.INSTANCE);\n\t\t\tif (currentSession != null) {\n\t\t\t\treturn Mono.from(currentSession.closeGracefully());\n\t\t\t}\n\t\t\treturn Mono.empty();\n\t\t});\n\t}\n\n\tprivate Mono<Disposable> reconnect(@Nullable McpTransportStream<Disposable> stream) {\n\t\treturn Mono.deferContextual(ctx -> {\n\t\t\tvar rh = this.handler.get();\n\t\t\tif (rh == null) {\n\t\t\t\tlogger.warn(\"Transport has no request handler registered. Remember to call connect!\");\n\t\t\t}\n\n\t\t\tfinal Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> requestHandler = rh != null\n\t\t\t\t\t? rh : msg -> Mono.error(new IllegalStateException(\"No request handler\"));\n\n\t\t\tfinal McpTransportSession<Disposable> transportSession = this.activeSession.get();\n\n\t\t\tif (ClosedMcpTransportSession.INSTANCE.equals(transportSession)) {\n\t\t\t\tthrow new McpTransportSessionClosedException();\n\t\t\t}\n\t\t\tif (stream != null) {\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Reconnecting stream \" + stream.streamId() + \" with lastId \" + stream.lastId());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlogger.debug(\"Reconnecting with no prior stream\");\n\t\t\t}","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/transport/mcp-spring-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/transport/WebClientStreamableHttpTransport.java#L213-L249","documentation":"A warning logged in WebClientStreamableHttpTransport.reconnect when the transport's request handler (set during connect()) is absent. Messages cannot be routed to the MCP session handler, so the code falls back to a request handler that errors every message with IllegalStateException(\"No request handler\"). This happens when reconnect() runs before or after the transport was properly connected.","triggerScenarios":"Calling reconnect() (directly or via stream re-establishment) on a transport whose connect() was never invoked; connect() was disposed/aborted, clearing the handler reference; a race where the SSE stream reconnects after the client shut down.","commonSituations":"Managing transport lifecycle manually and forgetting connect(); shutdown ordering issues where streams reconnect during application teardown; failed initialization leaving the handler unset but the stream retrying.","solutions":["Always call transport.connect() before sending messages or relying on streams","Ensure the connect() Mono is subscribed to and completed (it registers the handler) before traffic flows","Check shutdown ordering so reconnects are not triggered after disposal","Inspect lifecycle code for disposing the transport while its SSE stream auto-reconnects"],"exampleFix":"// before\nWebClientStreamableHttpTransport transport = WebClientStreamableHttpTransport.builder().build();\ntransport.sendMessage(message).block(); // handler not registered yet\n// after\nWebClientStreamableHttpTransport transport = WebClientStreamableHttpTransport.builder().build();\ntransport.connect().block(); // registers the request handler\ntransport.sendMessage(message).block();","handlingStrategy":"type-guard","validationCode":"// only proceed after connect completes\ntransport.connect().block(Duration.ofSeconds(10));","typeGuard":"boolean isConnected(WebClientStreamableHttpTransport t) {\n    try {\n        var f = WebClientStreamableHttpTransport.class.getDeclaredField(\"handler\");\n        f.setAccessible(true);\n        return ((java.util.concurrent.atomic.AtomicReference<?>) f.get(t)).get() != null;\n    } catch (Exception e) { return false; }\n}","tryCatchPattern":"try {\n    transport.sendMessage(msg).block();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"No request handler\")) {\n        transport.connect().block();\n    }\n}","preventionTips":["Always call and await connect() before sending","Sequence sends with connect().then(...)","Handle connect() error signals explicitly","Avoid disposing the transport while streams may auto-reconnect"],"tags":["mcp","transport","lifecycle","initialization"],"backgroundTag":"invalid-state-transition","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}