spring-projects/spring-ai · warning
Transport has no request handler registered. Remember to cal
Error message
Transport has no request handler registered. Remember to call connect!
What it means
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.
Source
Thrown at mcp/transport/mcp-spring-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/transport/WebClientStreamableHttpTransport.java:231
@Override
public Mono<Void> closeGracefully() {
return Mono.defer(() -> {
logger.debug("Graceful close triggered");
McpTransportSession<Disposable> currentSession = this.activeSession
.getAndSet(ClosedMcpTransportSession.INSTANCE);
if (currentSession != null) {
return Mono.from(currentSession.closeGracefully());
}
return Mono.empty();
});
}
private Mono<Disposable> reconnect(@Nullable McpTransportStream<Disposable> stream) {
return Mono.deferContextual(ctx -> {
var rh = this.handler.get();
if (rh == null) {
logger.warn("Transport has no request handler registered. Remember to call connect!");
}
final Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> requestHandler = rh != null
? rh : msg -> Mono.error(new IllegalStateException("No request handler"));
final McpTransportSession<Disposable> transportSession = this.activeSession.get();
if (ClosedMcpTransportSession.INSTANCE.equals(transportSession)) {
throw new McpTransportSessionClosedException();
}
if (stream != null) {
if (logger.isDebugEnabled()) {
logger.debug("Reconnecting stream " + stream.streamId() + " with lastId " + stream.lastId());
}
}
else {
logger.debug("Reconnecting with no prior stream");
}View on GitHub (pinned to 98a7beda4f)
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
Example fix
// before WebClientStreamableHttpTransport transport = WebClientStreamableHttpTransport.builder().build(); transport.sendMessage(message).block(); // handler not registered yet // after WebClientStreamableHttpTransport transport = WebClientStreamableHttpTransport.builder().build(); transport.connect().block(); // registers the request handler transport.sendMessage(message).block();
Defensive patterns
Strategy: type-guard
Validate before calling
// only proceed after connect completes transport.connect().block(Duration.ofSeconds(10));
Type guard
boolean isConnected(WebClientStreamableHttpTransport t) {
try {
var f = WebClientStreamableHttpTransport.class.getDeclaredField("handler");
f.setAccessible(true);
return ((java.util.concurrent.atomic.AtomicReference<?>) f.get(t)).get() != null;
} catch (Exception e) { return false; }
} Try / catch
try {
transport.sendMessage(msg).block();
} catch (IllegalStateException e) {
if (e.getMessage().contains("No request handler")) {
transport.connect().block();
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- McpTransportSessionClosedException
- Got error when closing transport
- Failed to read stdio connection resource
- SSE connection '<connectionName>' requires a 'url' property.
- Failed to create SSE transport for connection '<connectionNa
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/94178a56cd15ce93.
Report an issue: GitHub.