{"record":{"id":"95ada6e104d84a2d","repo":"spring-projects/spring-ai","slug":"mutate-must-be-overridden-to-return-the-most-con","errorCode":null,"errorMessage":"mutate() must be overridden to return the most concrete Builder","messagePattern":"mutate\\(\\) must be overridden to return the most concrete Builder","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/api/BaseAdvisorChain.java","lineNumber":46,"sourceCode":" *\n * @author Thomas Vitale\n * @author Christian Tzolov\n * @since 1.0.0\n */\npublic interface BaseAdvisorChain extends CallAdvisorChain, StreamAdvisorChain {\n\n\t/**\n\t * Returns a new {@link Builder} initialized with this chain's advisors and\n\t * configuration, allowing it to be selectively modified before building a new chain.\n\t *\n\t * <p>\n\t * Concrete {@link BaseAdvisorChain} classes must override this to return the most\n\t * concrete builder implementation.\n\t * @return a pre-populated {@link Builder}\n\t */\n\t// TODO: change from default to abstract once all implementations override mutate()\n\tdefault Builder<?> mutate() {\n\t\tthrow new UnsupportedOperationException(\"mutate() must be overridden to return the most concrete Builder\");\n\t}\n\n\t/**\n\t * Creates a new {@link Builder} for the default {@link BaseAdvisorChain}\n\t * implementation.\n\t * @param observationRegistry the observation registry to use\n\t * @return a new {@link Builder}\n\t */\n\tstatic Builder<?> builder(ObservationRegistry observationRegistry) {\n\t\treturn new DefaultAroundAdvisorChain.Builder(observationRegistry);\n\t}\n\n\t/**\n\t * Builder for creating a {@link BaseAdvisorChain} instance.\n\t *\n\t * @param <B> the concrete builder type, enabling fluent subtype chaining\n\t */\n\tinterface Builder<B extends Builder<B>> {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/api/BaseAdvisorChain.java#L28-L64","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Upgrade to a Spring AI version where your chain implementation overrides mutate(), or update the library.","In custom chain implementations, override mutate() to return the concrete Builder type.","As a workaround, rebuild the chain from scratch with a static builder instead of calling mutate()."],"exampleFix":"// before\nclass MyChain implements BaseAdvisorChain { /* no mutate() override */ }\n// after\nclass MyChain implements BaseAdvisorChain {\n  @Override\n  public Builder<MyChain.Builder> mutate() { return MyChain.builder().advisors(this.getAdvisors()); }\n}","handlingStrategy":"try-catch","validationCode":"if (chain.getClass().getMethods().stream().noneMatch(m -> m.getName().equals(\"mutate\") && m.getDeclaringClass() != BaseAdvisorChain.class)) {\n  // chain does not override mutate(); rebuild manually instead\n}","typeGuard":null,"tryCatchPattern":"try { newChain = chain.mutate(); } catch (UnsupportedOperationException e) { newChain = rebuildChainManually(chain); }","preventionTips":["Upgrade Spring AI so shipped chains override mutate().","Override mutate() in any custom BaseAdvisorChain implementation.","Prefer explicit static builders over mutate() for third-party chain classes."],"tags":["unsupported-operation","advisor-chain","spring-ai"],"backgroundTag":"method-not-implemented","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"}