spring-projects/spring-ai · error · IllegalStateException

Either delegate or toolObject must be set

Error message

Either delegate or toolObject must be set

What it means

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.

Source

Thrown at spring-ai-model/src/main/java/org/springframework/ai/tool/augment/AugmentedToolCallbackProvider.java:158

		/**
		 * Builds the {@link AugmentedToolCallbackProvider} instance.
		 * @return the built instance
		 * @throws IllegalStateException if required fields are not set
		 */
		public AugmentedToolCallbackProvider<T> build() {
			if (this.argumentType == null) {
				throw new IllegalStateException("argumentType is required");
			}
			if (this.argumentConsumer == null) {
				throw new IllegalStateException("argumentConsumer is required");
			}

			if (this.delegate != null && this.toolObject != null) {
				throw new IllegalStateException("Cannot set both delegate and toolObject");
			}

			if (this.delegate == null && this.toolObject == null) {
				throw new IllegalStateException("Either delegate or toolObject must be set");
			}

			if (this.toolObject != null) {
				return new AugmentedToolCallbackProvider<>(this.toolObject, this.argumentType, this.argumentConsumer,
						this.removeExtraArgumentsAfterProcessing);
			}
			else if (this.delegate != null) { // Redundant if condition to please NullAway
				return new AugmentedToolCallbackProvider<>(this.delegate, this.argumentType, this.argumentConsumer,
						this.removeExtraArgumentsAfterProcessing);
			}
			else {
				throw new IllegalStateException();
			}
		}

	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Set a source: call .toolObject(new MyTools()) or .delegate(toolCallbackProvider) on the builder.
  2. Verify configuration/properties actually resolve to a non-null tool source before building.
  3. If tools may legitimately be absent, skip creating the provider instead of building an empty one.

Example fix

// before
var builder = AugmentedToolCallbackProvider.builder()
    .argumentType(Args.class)
    .argumentConsumer(a -> a);
if (toolsEnabled) { /* source never set */ }
var provider = builder.build();
// after
var builder = AugmentedToolCallbackProvider.builder()
    .argumentType(Args.class)
    .argumentConsumer(a -> a);
if (toolsEnabled) {
    builder.toolObject(new MyTools());
    var provider = builder.build();
}
Defensive patterns

Strategy: validation

Validate before calling

if (toolSource == null) {
    throw new IllegalArgumentException("Provide a toolObject or delegate before building AugmentedToolCallbackProvider");
}

Type guard

if (toolObject == null && delegate == null) return null; // skip provider creation

Try / catch

try { provider = builder.build(); } catch (IllegalStateException e) { log.warn("No tool source configured, skipping augmented tools"); provider = null; }

Prevention

When it happens

Trigger: Calling AugmentedToolCallbackProvider.builder().argumentType(X.class).argumentConsumer(c -> c).build() without calling .delegate(...) or .toolObject(...).

Common situations: Conditional configuration where the source assignment branch was skipped; bean definitions parameterized by properties where the source property is empty/null.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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