spring-projects/spring-ai · error · IllegalArgumentException

Request must not be null

Error message

Request must not be null

What it means

SyncMcpResourceMethodCallback.apply requires a non-null ReadResourceRequest because it immediately reads request.uri() to extract URI variables. A null request would cause an NPE deeper in, so the callback fails fast with IllegalArgumentException('Request must not be null').

Source

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

	}

	/**
	 * Apply the callback to the given exchange and request.
	 * <p>
	 * This method extracts URI variable values from the request URI, builds the arguments
	 * for the method call, invokes the method, and converts the result to a
	 * ReadResourceResult.
	 * @param exchange The server exchange, may be null if the method doesn't require it
	 * @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);

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Construct and pass a ReadResourceRequest with the resource URI before calling apply.
  2. Add a null check at the call site and reject the request upstream with a proper error response.
  3. In tests, use a fixture/builder that always produces a valid ReadResourceRequest.

Example fix

// before
callback.apply(exchange, null);

// after
if (request == null) { return errorResponse("read missing request"); }
callback.apply(exchange, new ReadResourceRequest(new McpResourceUri("docs://readme")));
Defensive patterns

Strategy: validation

Validate before calling

if (request == null) {
    throw new McpError("read resource failed: request must not be null");
}

Type guard

static boolean validRequest(ReadResourceRequest r) {
    return r != null && r.uri() != null;
}

Try / catch

try {
    return callback.apply(exchange, request);
} catch (IllegalArgumentException e) {
    if ("Request must not be null".equals(e.getMessage())) {
        log.warn("Dropped null read-resource request");
        return new ReadResourceResult(List.of());
    } throw e;
}

Prevention

When it happens

Trigger: Calling apply(exchange, null) directly — typically in unit tests, custom dispatchers, or wrapper code around the resource callback.

Common situations: Hand-written test harnesses invoking the callback, or custom transport glue that forgets to construct the ReadResourceRequest when forwarding a read-resource call.

Related errors


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