spring-projects/spring-ai · error · IllegalArgumentException

Failed to extract all URI variables from request URI: {uri}.

Error message

Failed to extract all URI variables from request URI: {uri}. Expected variables: {expectedVariables}, but found: {foundVariables}

What it means

After extracting URI variables from the request URI via UriTemplateManager, apply verifies that the number of extracted values equals the number of URI variables declared on the method's @McpResource URI template. A mismatch means the incoming request URI did not provide values for all declared template variables, so the method cannot be invoked safely and IllegalArgumentException is thrown.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/SyncMcpResourceMethodCallback.java:123

	 * @param request The resource request, must not be null
	 * @return The resource result
	 * @throws McpError if there is an error invoking the resource method
	 * @throws IllegalArgumentException if the request is null or if URI variable
	 * extraction fails
	 */
	@Override
	public ReadResourceResult apply(McpSyncServerExchange exchange, ReadResourceRequest request) {
		if (request == null) {
			throw new IllegalArgumentException("Request must not be null");
		}

		try {
			// Extract URI variable values from the request URI
			Map<String, String> uriVariableValues = this.uriTemplateManager.extractVariableValues(request.uri());

			// Verify all URI variables were extracted if URI variables are expected
			if (!this.uriVariables.isEmpty() && uriVariableValues.size() != this.uriVariables.size()) {
				throw new IllegalArgumentException("Failed to extract all URI variables from request URI: "
						+ request.uri() + ". Expected variables: " + this.uriVariables + ", but found: "
						+ uriVariableValues.keySet());
			}

			// Build arguments for the method call
			Object[] args = this.buildArgs(this.method, exchange, request, uriVariableValues);

			// Invoke the method
			this.method.setAccessible(true);
			Object result = this.method.invoke(this.bean, args);

			// Convert the result to a ReadResourceResult using the converter
			return this.resultConverter.convertToReadResourceResult(result, request.uri(), this.mimeType,
					this.contentType, this.meta);
		}
		catch (Exception e) {
			if (e instanceof McpError mcpError && mcpError.getJsonRpcError() != null) {
				throw mcpError;

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Check the request URI matches the full template; ensure every {var} segment has a value.
  2. Verify URI encoding — decode or normalize the URI so variable extraction works (e.g., percent-encoded slashes).
  3. If clients use an older URI shape, keep backward-compatible templates or register an additional resource for the legacy URI.
  4. Log the expected vs. found variable sets to identify which variable failed to extract.

Example fix

// before (request)
client.readResource(new McpResourceUri("wiki://java")); // template wiki://{lang}/{topic}

// after
client.readResource(new McpResourceUri("wiki://java/collections"));
Defensive patterns

Strategy: validation

Validate before calling

// verify the URI satisfies the template before calling
UriTemplateManager mgr = new UriTemplateManager("wiki://{lang}/{topic}");
Map<String,String> vars = mgr.extractVariableValues(requestUri);
if (vars.size() != mgr.getVariableNames().size()) {
    throw new McpError("URI " + requestUri + " does not match template variables " + mgr.getVariableNames());
}

Type guard

static boolean uriMatchesAllVars(UriTemplateManager mgr, String uri) {
    Map<String,String> got = mgr.extractVariableValues(uri);
    return got != null && got.size() == mgr.getVariableNames().size();
}

Try / catch

try {
    return callback.apply(exchange, request);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Failed to extract all URI variables")) {
        log.error("Request URI does not satisfy the resource template: " + request.uri(), e);
        throw new McpError("Invalid resource URI: " + request.uri());
    } throw e;
}

Prevention

When it happens

Trigger: A ReadResourceRequest whose uri() fails to match all variables of the registered template, e.g., template 'wiki://{lang}/{topic}' invoked with 'wiki://topic-only' — one variable missing, sizes differ.

Common situations: Client sends a malformed/partial resource URI, URI encoding quirks (e.g., encoded slashes, trailing characters) break extraction, or the template was changed server-side while clients use the old URI shape.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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