{"record":{"id":"8f58e7975e934571","repo":"spring-projects/spring-ai","slug":"bean-must-not-be-null-8f58e7","errorCode":null,"errorMessage":"Bean must not be null","messagePattern":"Bean 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/sampling/AbstractMcpSamplingMethodCallback.java","lineNumber":239,"sourceCode":"\t\t * @param sampling The sampling annotation\n\t\t * @return This builder\n\t\t */\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic T sampling(McpSampling sampling) {\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":221,"sourceCodeEnd":252,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/sampling/AbstractMcpSamplingMethodCallback.java#L221-L252","documentation":"Abstract builder validate() throws IllegalArgumentException when either the callback's Method or its target bean instance is null. Every sampling method callback builder (sync and async) requires both a reflective Method handle and the bean object it belongs to before it can invoke anything. This is a programming error caught at build time, before any sampling request is served.","triggerScenarios":"Calling .method(m) without .bean(obj), or .bean(obj) without .method(m), then invoking build() on an AbstractMcpSamplingMethodCallback.Builder subclass.","commonSituations":"Programmatically assembling sampling callbacks where one builder field is conditionally set; refactoring code that drops the bean assignment; building callbacks in a loop where a null element slips in.","solutions":["Set both method and bean on the builder before calling build()","If discovering methods reflectively, verify the declaring class is itself the bean instance passed in","Wrap builder calls in a helper that asserts non-null method/bean pairs"],"exampleFix":"// before\nMcpSamplingMethodCallback cb = new AsyncMcpSamplingMethodCallback.Builder()\n    .method(method)\n    .build();\n// after\nMcpSamplingMethodCallback cb = new AsyncMcpSamplingMethodCallback.Builder()\n    .method(method)\n    .bean(myService)\n    .build();","handlingStrategy":"validation","validationCode":"if (method == null || bean == null) {\n    throw new IllegalStateException(\"Sampling callback needs both method and bean\");\n}\nnew AsyncMcpSamplingMethodCallback.Builder().method(method).bean(bean).build();","typeGuard":"boolean isReady(Builder b) { return b.method != null && b.bean != null; }","tryCatchPattern":null,"preventionTips":["Always set bean and method together in a small factory helper","Write a unit test that builds each registered callback at startup","Fail fast in application startup if any callback construction throws"],"tags":["null-check","builder","sampling"],"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"}