spring-projects/spring-ai · error · IllegalStateException
Roots not supported by the client:
Error message
Roots not supported by the client:
What it means
DefaultMcpSyncRequestContext.roots() requests the client's filesystem roots via the MCP roots capability. MCP clients must advertise the roots capability during initialization; when this client did not, the server cannot call listRoots, so an IllegalStateException is thrown including the client's ClientInfo.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/context/DefaultMcpSyncRequestContext.java:80
private DefaultMcpSyncRequestContext(McpSchema.Request request, McpSyncServerExchange exchange) {
Assert.notNull(request, "Request must not be null");
Assert.notNull(exchange, "Exchange must not be null");
this.request = request;
this.exchange = exchange;
}
// Roots
@Override
public boolean rootsEnabled() {
return !(this.exchange.getClientCapabilities() == null
|| this.exchange.getClientCapabilities().roots() == null);
}
@Override
public ListRootsResult roots() {
if (!this.rootsEnabled()) {
throw new IllegalStateException("Roots not supported by the client: " + this.exchange.getClientInfo());
}
return this.exchange.listRoots();
}
// Elicitation
@Override
public boolean elicitEnabled() {
return !(this.exchange.getClientCapabilities() == null
|| this.exchange.getClientCapabilities().elicitation() == null);
}
@Override
public <T> StructuredElicitResult<T> elicit(Class<T> type) {
if (!this.elicitEnabled()) {
throw new IllegalStateException(
"Elicitation not supported by the client: " + this.exchange.getClientInfo());View on GitHub (pinned to 98a7beda4f)
Solutions
- Enable the roots capability on the MCP client during initialization (e.g. set ClientCapabilities.builder().roots(true) or equivalent)
- Check this.rootsEnabled() / context capability before calling roots() and degrade gracefully when unsupported
- Upgrade or replace the client library/host if it does not support roots
Example fix
// before
var roots = context.roots();
// after
List<Root> roots = context.rootsEnabled()
? context.roots().roots()
: List.of(); // fallback: no roots support Defensive patterns
Strategy: try-catch
Validate before calling
if (context.getClientCapabilities() == null
|| context.getClientCapabilities().roots() == null) {
return List.of(); // skip roots-dependent logic
} Type guard
boolean rootsSupported(DefaultMcpSyncRequestContext ctx) {
return ctx.rootsEnabled();
} Try / catch
try {
ListRootsResult result = context.roots();
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Roots not supported")) {
// proceed without roots
} else {
throw e;
}
} Prevention
- Enable the roots capability on the client at initialization time
- Gate roots usage behind a capability check on the server side
- Document that server tools requiring roots need a roots-capable client
When it happens
Trigger: Server-side code calls context.roots() (or relies on roots implicitly) while handling a request from a client whose getClientCapabilities().roots() is null.
Common situations: Client (e.g. a minimal MCP host) initialized without declaring roots capability; older client library version that predates roots support; capability negotiation flags misconfigured on the client side.
Related errors
- Currently only methods with a single ElicitRequest parameter
- Method must return ElicitResult or StructuredElicitResult:
- Stateless tool methods do not support McpAsyncRequestContext
- Stateless tool methods do not support McpSyncRequestContext
- Failed to read stdio connection resource
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/8ec98eb9821b0540.
Report an issue: GitHub.