spring-projects/spring-ai · error · IllegalArgumentException

Updated tools list must not be null

Error message

Updated tools list must not be null

What it means

SyncMcpToolListChangedMethodCallback.accept implements the Consumer<List<McpSchema.Tool>> invoked when a client's tool list changes. A null updatedTools list cannot be dispatched to the user handler (the library guarantees a real list), so it throws IllegalArgumentException immediately with 'Updated tools list must not be null'.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/SyncMcpToolListChangedMethodCallback.java:55

		implements Consumer<List<McpSchema.Tool>> {

	private SyncMcpToolListChangedMethodCallback(Builder builder) {
		super(builder.method, builder.bean);
	}

	/**
	 * Accept the tool list change notification and process it.
	 * <p>
	 * This method builds the arguments for the method call and invokes the method.
	 * @param updatedTools The updated list of tools, must not be null
	 * @throws McpToolListChangedConsumerMethodException if there is an error invoking the
	 * tool list changed consumer method
	 * @throws IllegalArgumentException if the updatedTools is null
	 */
	@Override
	public void accept(List<McpSchema.Tool> updatedTools) {
		if (updatedTools == null) {
			throw new IllegalArgumentException("Updated tools list must not be null");
		}

		try {
			// Build arguments for the method call
			Object[] args = this.buildArgs(this.method, null, updatedTools);

			// Invoke the method
			this.method.setAccessible(true);
			this.method.invoke(this.bean, args);
		}
		catch (Exception e) {
			throw new McpToolListChangedConsumerMethodException(
					"Error invoking tool list changed consumer method: " + this.method.getName(), e);
		}
	}

	/**
	 * Validates that the method return type is compatible with the tool list changed

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Never call accept(null); pass the actual list from the client, using List.of() (empty list) when there are no tools
  2. Add a caller-side null/empty check before invoking the consumer and substitute an empty list for null
  3. In test setups, use an empty List<McpSchema.Tool> instead of null to simulate 'no tools'

Example fix

// before
callback.accept(null); // throws IllegalArgumentException
// after
java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> tools =
    (raw == null) ? java.util.List.of() : raw;
callback.accept(tools);
Defensive patterns

Strategy: validation

Validate before calling

java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> safeTools = java.util.Objects.requireNonNullElse(updatedTools, java.util.List.of());

Type guard

static java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> nonNullTools(java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> l) { return l == null ? java.util.List.of() : l; }

Try / catch

try { callback.accept(updatedTools); } catch (IllegalArgumentException e) { log.error("Tool list callback rejected input: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Invoking accept(null) — typically when custom plumbing or a test feeds the consumer directly, or when a wrapper/adapter constructs the updated-tools list from a nullable source instead of the client's tool list.

Common situations: Unit tests calling the callback with null to check behavior, custom event bridges forwarding a null payload from another notification source, or misconfigured clients that emit change events without computing the new tool list.

Related errors


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