spring-projects/spring-ai · warning
Notification: + sentMessage + received non-compliant respons
Error message
Notification: + sentMessage + received non-compliant response: + responseMessage
What it means
A warning in directResponseFlux when an HTTP POST returning a direct (non-stream) response body gets a body even though the sent message was a JSON-RPC notification. Per the MCP Streamable HTTP spec, notifications must receive 202 with no body, so any non-empty body is flagged as a non-compliant response and the flux completes normally, discarding the body.
Source
Thrown at mcp/transport/mcp-spring-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/transport/WebClientStreamableHttpTransport.java:519
return response.statusCode().isSameCodeAs(HttpStatus.METHOD_NOT_ALLOWED);
}
private static boolean isEventStream(ClientResponse response) {
return response.statusCode().is2xxSuccessful() && response.headers().contentType().isPresent()
&& response.headers().contentType().get().isCompatibleWith(MediaType.TEXT_EVENT_STREAM);
}
private static String sessionIdOrPlaceholder(McpTransportSession<?> transportSession) {
return transportSession.sessionId().orElse(MISSING_SESSION_ID);
}
private Flux<McpSchema.JSONRPCMessage> directResponseFlux(McpSchema.JSONRPCMessage sentMessage,
ClientResponse response) {
return response.bodyToMono(String.class).<Iterable<McpSchema.JSONRPCMessage>>handle((responseMessage, s) -> {
try {
if (sentMessage instanceof McpSchema.JSONRPCNotification) {
if (logger.isWarnEnabled()) {
logger.warn("Notification: " + sentMessage + " received non-compliant response: "
+ (Utils.hasText(responseMessage) ? responseMessage : "[empty]"));
}
s.complete();
}
else {
McpSchema.JSONRPCMessage jsonRpcResponse = McpSchema.deserializeJsonRpcMessage(this.jsonMapper,
responseMessage);
s.next(List.of(jsonRpcResponse));
}
}
catch (IOException e) {
s.error(new McpTransportException(e));
}
}).flatMapIterable(Function.identity());
}
private Flux<McpSchema.JSONRPCMessage> newEventStream(ClientResponse response, String sessionRepresentation) {
McpTransportStream<Disposable> sessionStream = new DefaultMcpTransportStream<>(this.resumableStreams,View on GitHub (pinned to 98a7beda4f)
Solutions
- Fix the server (or update its MCP transport) to return HTTP 202 with an empty body for notifications
- Check intermediary proxies/gateways for response body injection or status rewriting
- Ignore the warning if harmless, but verify notifications are actually processed server-side
- Test with the reference MCP server implementation to isolate compliance issues
Example fix
// before
// server: return ok().body("accepted", String.class) for notifications
// after
// server: return ResponseEntity.accepted().build(); // 202, no body for notifications Defensive patterns
Strategy: fallback
Type guard
boolean isNotification(McpSchema.JSONRPCMessage m) {
return m instanceof McpSchema.JSONRPCNotification;
}
// only send notifications expecting no response body Prevention
- Return 202 with an empty body for notification POSTs in custom servers
- Audit proxies for response rewriting
- Test against the reference MCP server for compliance
- Keep server transport libraries up to date
When it happens
Trigger: Server responds 200 with a JSON body to a notification POST; a proxy rewrites 202 responses into 200 with a body; server transport implementation not following the notification-202 rule.
Common situations: Custom or hand-rolled MCP servers replying to notifications; gateway/proxy middleware adding response bodies; older server versions predating strict Streamable HTTP compliance.
Related errors
- Stateless Streamable-Http prompt method must not declare par
- Server does not recognize session + invalidSession.sessionId
- Unknown media type + contentType + returned for POST in sess
- Session + sessionRepresentation + was not found on the MCP s
- HTTP %s - %s
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/9de1d6d45c1387fe.
Report an issue: GitHub.