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

  1. Enable the roots capability on the MCP client during initialization (e.g. set ClientCapabilities.builder().roots(true) or equivalent)
  2. Check this.rootsEnabled() / context capability before calling roots() and degrade gracefully when unsupported
  3. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/8ec98eb9821b0540. Report an issue: GitHub.