{"record":{"id":"7d12f9a0fbbb2890","repo":"spring-projects/spring-ai","slug":"bean-must-not-be-null-7d12f9","errorCode":null,"errorMessage":"Bean must not be null","messagePattern":"Bean must not be null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/AbstractMcpToolListChangedMethodCallback.java","lineNumber":211,"sourceCode":"\t\t * @param toolListChanged The tool list changed annotation\n\t\t * @return This builder\n\t\t */\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic T toolListChanged(McpToolListChanged toolListChanged) {\n\t\t\t// No additional configuration needed from the annotation at this time\n\t\t\treturn (T) this;\n\t\t}\n\n\t\t/**\n\t\t * Validate the builder state.\n\t\t * @throws IllegalArgumentException if the builder state is invalid\n\t\t */\n\t\tprotected void validate() {\n\t\t\tif (this.method == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Method must not be null\");\n\t\t\t}\n\t\t\tif (this.bean == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bean must not be null\");\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Build the callback.\n\t\t * @return A new callback instance\n\t\t */\n\t\tpublic abstract R build();\n\n\t}\n\n}\n","sourceCodeStart":193,"sourceCodeEnd":224,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/AbstractMcpToolListChangedMethodCallback.java#L193-L224","documentation":"The same builder validate() step also requires `bean` — the object instance whose method will be reflectively invoked on each tool-list-changed notification. Without a bean instance, calling method.invoke would have no target, so the library throws IllegalArgumentException during build().","triggerScenarios":"Calling builder.build() with .method(m) set but no .bean(instance), e.g. building from a Class instead of an instance: new Builder().method(beanClass.getMethod(...)).build() — bean == null triggers this message.","commonSituations":"Developers assuming static-method handlers need no bean; the library still requires a target object. Also common when the bean is created lazily or by a context that hasn't initialized yet, leaving the field null.","solutions":["Call .bean(instance) on the builder, passing the actual bean that owns the handler method","For static-style handlers, still pass an instance (e.g. an empty holder) since Method.invoke requires a receiver for instance methods; or declare the handler as a real instance method","Verify bean initialization order so the bean is non-null at registration time (e.g. inject it rather than reading a nullable field)"],"exampleFix":"// before\nnew SyncMcpToolListChangedMethodCallback.Builder().method(m).build();\n// after\nnew SyncMcpToolListChangedMethodCallback.Builder().bean(myHandler).method(m).build();","handlingStrategy":"validation","validationCode":"java.util.Objects.requireNonNull(bean, \"builder.bean(Object) is required before build()\");","typeGuard":"static boolean beanReady(Object bean) { return bean != null; }","tryCatchPattern":"try { return builder.bean(bean).method(method).build(); } catch (IllegalArgumentException e) { throw new IllegalStateException(\"Builder missing bean: \" + e.getMessage(), e); }","preventionTips":["Ensure the handler bean is initialized before registration (check Spring bean lifecycle/order)","Never build callbacks from a Class reference alone; use an instance","Add an assertion in the registration helper that both bean and method are supplied"],"tags":["java","builder-pattern","null","validation"],"backgroundTag":"missing-required-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"}