spring-projects/spring-ai · error · UnsupportedOperationException

mutate() must be overridden to return the most concrete Buil

Error message

mutate() must be overridden to return the most concrete Builder

What it means

BaseAdvisorChain.mutate() is a default method meant to be overridden by concrete advisor chains to return their most specific Builder. The default implementation throws UnsupportedOperationException('mutate() must be overridden to return the most concrete Builder'), so calling it on a chain that hasn't overridden mutate() always fails.

Source

Thrown at spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/api/BaseAdvisorChain.java:46

 *
 * @author Thomas Vitale
 * @author Christian Tzolov
 * @since 1.0.0
 */
public interface BaseAdvisorChain extends CallAdvisorChain, StreamAdvisorChain {

	/**
	 * Returns a new {@link Builder} initialized with this chain's advisors and
	 * configuration, allowing it to be selectively modified before building a new chain.
	 *
	 * <p>
	 * Concrete {@link BaseAdvisorChain} classes must override this to return the most
	 * concrete builder implementation.
	 * @return a pre-populated {@link Builder}
	 */
	// TODO: change from default to abstract once all implementations override mutate()
	default Builder<?> mutate() {
		throw new UnsupportedOperationException("mutate() must be overridden to return the most concrete Builder");
	}

	/**
	 * Creates a new {@link Builder} for the default {@link BaseAdvisorChain}
	 * implementation.
	 * @param observationRegistry the observation registry to use
	 * @return a new {@link Builder}
	 */
	static Builder<?> builder(ObservationRegistry observationRegistry) {
		return new DefaultAroundAdvisorChain.Builder(observationRegistry);
	}

	/**
	 * Builder for creating a {@link BaseAdvisorChain} instance.
	 *
	 * @param <B> the concrete builder type, enabling fluent subtype chaining
	 */
	interface Builder<B extends Builder<B>> {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Upgrade to a Spring AI version where your chain implementation overrides mutate(), or update the library.
  2. In custom chain implementations, override mutate() to return the concrete Builder type.
  3. As a workaround, rebuild the chain from scratch with a static builder instead of calling mutate().

Example fix

// before
class MyChain implements BaseAdvisorChain { /* no mutate() override */ }
// after
class MyChain implements BaseAdvisorChain {
  @Override
  public Builder<MyChain.Builder> mutate() { return MyChain.builder().advisors(this.getAdvisors()); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (chain.getClass().getMethods().stream().noneMatch(m -> m.getName().equals("mutate") && m.getDeclaringClass() != BaseAdvisorChain.class)) {
  // chain does not override mutate(); rebuild manually instead
}

Try / catch

try { newChain = chain.mutate(); } catch (UnsupportedOperationException e) { newChain = rebuildChainManually(chain); }

Prevention

When it happens

Trigger: Calling mutate() (e.g. via copy-on-write advisor chain customization) on a BaseAdvisorChain implementation that still uses the default method; also hit by default-implementation callers until all implementations override mutate() (noted by the TODO in the source).

Common situations: Third-party or custom AdvisorChain implementation that hasn't implemented mutate(); upgrading Spring AI where new code paths call mutate() on older/custom chains; using copy/mutate on a chain class that only partially implements the API.

Related errors


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