{"record":{"id":"3dffd052f3c39fc7","repo":"spring-projects/spring-ai","slug":"updated-tools-list-must-not-be-null","errorCode":null,"errorMessage":"Updated tools list must not be null","messagePattern":"Updated tools list must not be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/SyncMcpToolListChangedMethodCallback.java","lineNumber":55,"sourceCode":"\t\timplements Consumer<List<McpSchema.Tool>> {\n\n\tprivate SyncMcpToolListChangedMethodCallback(Builder builder) {\n\t\tsuper(builder.method, builder.bean);\n\t}\n\n\t/**\n\t * Accept the tool list change notification and process it.\n\t * <p>\n\t * This method builds the arguments for the method call and invokes the method.\n\t * @param updatedTools The updated list of tools, must not be null\n\t * @throws McpToolListChangedConsumerMethodException if there is an error invoking the\n\t * tool list changed consumer method\n\t * @throws IllegalArgumentException if the updatedTools is null\n\t */\n\t@Override\n\tpublic void accept(List<McpSchema.Tool> updatedTools) {\n\t\tif (updatedTools == null) {\n\t\t\tthrow new IllegalArgumentException(\"Updated tools list must not be null\");\n\t\t}\n\n\t\ttry {\n\t\t\t// Build arguments for the method call\n\t\t\tObject[] args = this.buildArgs(this.method, null, updatedTools);\n\n\t\t\t// Invoke the method\n\t\t\tthis.method.setAccessible(true);\n\t\t\tthis.method.invoke(this.bean, args);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new McpToolListChangedConsumerMethodException(\n\t\t\t\t\t\"Error invoking tool list changed consumer method: \" + this.method.getName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Validates that the method return type is compatible with the tool list changed","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/SyncMcpToolListChangedMethodCallback.java#L37-L73","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Never call accept(null); pass the actual list from the client, using List.of() (empty list) when there are no tools","Add a caller-side null/empty check before invoking the consumer and substitute an empty list for null","In test setups, use an empty List<McpSchema.Tool> instead of null to simulate 'no tools'"],"exampleFix":"// before\ncallback.accept(null); // throws IllegalArgumentException\n// after\njava.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> tools =\n    (raw == null) ? java.util.List.of() : raw;\ncallback.accept(tools);","handlingStrategy":"validation","validationCode":"java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> safeTools = java.util.Objects.requireNonNullElse(updatedTools, java.util.List.of());","typeGuard":"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; }","tryCatchPattern":"try { callback.accept(updatedTools); } catch (IllegalArgumentException e) { log.error(\"Tool list callback rejected input: {}\", e.getMessage()); }","preventionTips":["Pass the client-provided list directly rather than recomputing it from nullable sources","Use List.of() to represent 'no tools' instead of null","In tests, simulate empty tool lists with an empty list, not null"],"tags":["java","null","consumer","validation"],"backgroundTag":"null-argument","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}