{"record":{"id":"56616900531b7807","repo":"spring-projects/spring-ai","slug":"either-delegate-or-toolobject-must-be-set","errorCode":null,"errorMessage":"Either delegate or toolObject must be set","messagePattern":"Either delegate or toolObject must be set","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-model/src/main/java/org/springframework/ai/tool/augment/AugmentedToolCallbackProvider.java","lineNumber":158,"sourceCode":"\t\t/**\n\t\t * Builds the {@link AugmentedToolCallbackProvider} instance.\n\t\t * @return the built instance\n\t\t * @throws IllegalStateException if required fields are not set\n\t\t */\n\t\tpublic AugmentedToolCallbackProvider<T> build() {\n\t\t\tif (this.argumentType == null) {\n\t\t\t\tthrow new IllegalStateException(\"argumentType is required\");\n\t\t\t}\n\t\t\tif (this.argumentConsumer == null) {\n\t\t\t\tthrow new IllegalStateException(\"argumentConsumer is required\");\n\t\t\t}\n\n\t\t\tif (this.delegate != null && this.toolObject != null) {\n\t\t\t\tthrow new IllegalStateException(\"Cannot set both delegate and toolObject\");\n\t\t\t}\n\n\t\t\tif (this.delegate == null && this.toolObject == null) {\n\t\t\t\tthrow new IllegalStateException(\"Either delegate or toolObject must be set\");\n\t\t\t}\n\n\t\t\tif (this.toolObject != null) {\n\t\t\t\treturn new AugmentedToolCallbackProvider<>(this.toolObject, this.argumentType, this.argumentConsumer,\n\t\t\t\t\t\tthis.removeExtraArgumentsAfterProcessing);\n\t\t\t}\n\t\t\telse if (this.delegate != null) { // Redundant if condition to please NullAway\n\t\t\t\treturn new AugmentedToolCallbackProvider<>(this.delegate, this.argumentType, this.argumentConsumer,\n\t\t\t\t\t\tthis.removeExtraArgumentsAfterProcessing);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t}\n\t\t}\n\n\t}\n\n}","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-model/src/main/java/org/springframework/ai/tool/augment/AugmentedToolCallbackProvider.java#L140-L176","documentation":"build() requires at least one tool source: either a delegate ToolCallback/ToolCallbackProvider or a toolObject with @Tool methods. If neither is set the provider would produce no tools, so it throws an IllegalStateException immediately.","triggerScenarios":"Calling AugmentedToolCallbackProvider.builder().argumentType(X.class).argumentConsumer(c -> c).build() without calling .delegate(...) or .toolObject(...).","commonSituations":"Conditional configuration where the source assignment branch was skipped; bean definitions parameterized by properties where the source property is empty/null.","solutions":["Set a source: call .toolObject(new MyTools()) or .delegate(toolCallbackProvider) on the builder.","Verify configuration/properties actually resolve to a non-null tool source before building.","If tools may legitimately be absent, skip creating the provider instead of building an empty one."],"exampleFix":"// before\nvar builder = AugmentedToolCallbackProvider.builder()\n    .argumentType(Args.class)\n    .argumentConsumer(a -> a);\nif (toolsEnabled) { /* source never set */ }\nvar provider = builder.build();\n// after\nvar builder = AugmentedToolCallbackProvider.builder()\n    .argumentType(Args.class)\n    .argumentConsumer(a -> a);\nif (toolsEnabled) {\n    builder.toolObject(new MyTools());\n    var provider = builder.build();\n}","handlingStrategy":"validation","validationCode":"if (toolSource == null) {\n    throw new IllegalArgumentException(\"Provide a toolObject or delegate before building AugmentedToolCallbackProvider\");\n}","typeGuard":"if (toolObject == null && delegate == null) return null; // skip provider creation","tryCatchPattern":"try { provider = builder.build(); } catch (IllegalStateException e) { log.warn(\"No tool source configured, skipping augmented tools\"); provider = null; }","preventionTips":["Make tool sources mandatory constructor/bean parameters so they cannot silently be null.","Log configuration resolution results when building providers from properties."],"tags":["builder-validation","missing-argument","spring-ai"],"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-14T05:17:10.506Z"}