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

The request URI did not yield values for all URI template variables declared on the @McpResource method. The callback compares the extracted variable count to the declared variables and throws when they differ.

Source

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

	 * @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(McpTransportContext context, 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, context, 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. Send a request URI that fully matches the @McpResource uri template, filling every {variable}
  2. Verify the template placeholders match the method parameter names (this.uriVariables)
  3. Normalize/decode the request URI before it reaches the callback in custom transports
  4. Add integration tests that exercise the templated URI end to end

Example fix

// before
var result = client.readResource("file:///docs"); // template is file://{path}
// after
var result = client.readResource("file:///docs/report.txt");
Defensive patterns

Strategy: validation

Validate before calling

Set<String> declared = Set.of("path"); // from the @McpResource template
Map<String,String> vars = uriTemplateManager.extractVariableValues(request.uri());
if (vars.size() != declared.size()) {
    throw new IllegalArgumentException("URI " + request.uri() + " does not fill all template variables");
}

Type guard

boolean uriMatchesTemplate(String uri, String template) {
    return uri != null && !uri.isBlank() && uri.startsWith(template.substring(0, template.indexOf('{')));
}

Try / catch

try {
    return callback.apply(context, request);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Failed to extract all URI variables")) {
        return errorResult("Resource URI does not match template");
    }
    throw e;
}

Prevention

When it happens

Trigger: A ReadResourceRequest whose URI does not match the method's URI template (e.g. missing a path segment or query part that maps to a declared {var}), so extractVariableValues returns fewer variables than declared.

Common situations: Client calling the wrong resource URI; typos in URI template placeholders; registering a template with variables the caller omits; URI-encoding differences that break extraction.

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/6931ba03c7c39c0d. Report an issue: GitHub.