{"record":{"id":"aa4aca7efc2496e9","repo":"alibaba/spring-ai-alibaba","slug":"the-provided-chatoptions-is-not-of-type-toolcallin","errorCode":null,"errorMessage":"The provided chatOptions is not of type ToolCallingChatOptions (actual type: {}). It will not take effect. Creating a new ToolCallingChatOptions with toolCallbacks instead.","messagePattern":"The provided chatOptions is not of type ToolCallingChatOptions \\(actual type: (.+?)\\)\\. It will not take effect\\. Creating a new ToolCallingChatOptions with toolCallbacks instead\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java","lineNumber":371,"sourceCode":"\t\tif (chatOptions != null) {\n\t\t\tif (chatOptions instanceof ToolCallingChatOptions builderToolCallingOptions) {\n\t\t\t\tToolCallingChatOptions copiedOptions = builderToolCallingOptions.copy();\n\n\t\t\t\tList<ToolCallback> mergedToolCallbacks = new ArrayList<>(toolCallbacks);\n\t\t\t\t// Add callbacks from chatOptions that are not already present (toolCallbacks takes precedence)\n\t\t\t\tfor (ToolCallback callback : builderToolCallingOptions.getToolCallbacks()) {\n\t\t\t\t\tboolean exists = mergedToolCallbacks.stream()\n\t\t\t\t\t\t\t.anyMatch(tc -> tc.getToolDefinition().name().equals(callback.getToolDefinition().name()));\n\t\t\t\t\tif (!exists) {\n\t\t\t\t\t\tmergedToolCallbacks.add(callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcopiedOptions.setToolCallbacks(mergedToolCallbacks);\n\t\t\t\tcopiedOptions.setInternalToolExecutionEnabled(false);\n\t\t\t\treturn copiedOptions;\n\t\t\t} else {\n\t\t\t\tlogger.warn(\"The provided chatOptions is not of type ToolCallingChatOptions (actual type: {}). \" +\n\t\t\t\t\t\t\t\t\"It will not take effect. Creating a new ToolCallingChatOptions with toolCallbacks instead.\",\n\t\t\t\t\t\tchatOptions.getClass().getName());\n\t\t\t}\n\t\t}\n\n\t\treturn ToolCallingChatOptions.builder()\n\t\t\t\t.toolCallbacks(toolCallbacks)\n\t\t\t\t.internalToolExecutionEnabled(false)\n\t\t\t\t.build();\n\t}\n\n\tprivate String renderPromptTemplate(String prompt, Map<String, Object> params) {\n\t\tPromptTemplate.Builder builder = PromptTemplate.builder().template(prompt);\n\t\tif (templateRenderer != null) {\n\t\t\tbuilder.renderer(templateRenderer);\n\t\t}\n\t\treturn builder.build().render(params);\n\t}","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java#L353-L389","documentation":"AgentLlmNode.buildChatOptions expects chatOptions to implement ToolCallingChatOptions so tool callbacks can be attached and internal tool execution disabled. When a ChatOptions instance of another type is supplied, the node logs this warning and builds a fresh ToolCallingChatOptions with the tool callbacks — meaning any custom settings on the original options object (temperature, topP, model name, etc.) silently do NOT take effect.","triggerScenarios":"Passing a ChatOptions implementation that does not implement ToolCallingChatOptions into AgentLlmNode (e.g. a provider-specific options class like OpenAiChatOptions from a version that isn't a ToolCallingChatOptions, or a plain ChatOptions built via ChatOptions.builder()).","commonSituations":"1) Building options with ChatOptions.builder() instead of ToolCallingChatOptions.builder(). 2) A custom ChatOptions subclass missing the ToolCallingChatOptions interface. 3) Upgrading Spring AI and swapping to a different provider's options class that lost the interface. 4) Copying options into your own type before handing them to the node.","solutions":["Construct options with ToolCallingChatOptions.builder() so the instanceof branch succeeds and your settings are copied with tool callbacks merged.","If you need provider-specific options, ensure the class still implements ToolCallingChatOptions (most Spring AI provider options do).","After construction, assert options instanceof ToolCallingChatOptions in your own setup code to catch regressions early.","If you must keep the foreign options object, manually transfer its fields (temperature, model, etc.) onto the ToolCallingChatOptions the node creates."],"exampleFix":"// before\nChatOptions options = ChatOptions.builder().model(\"qwen-max\").build();\n// after\nToolCallingChatOptions options = ToolCallingChatOptions.builder()\n    .model(\"qwen-max\")\n    .toolCallbacks(myCallbacks)\n    .internalToolExecutionEnabled(false)\n    .build();","handlingStrategy":"type-guard","validationCode":"if (!(chatOptions instanceof ToolCallingChatOptions)) {\n    throw new IllegalArgumentException(\"chatOptions must be ToolCallingChatOptions, got: \" + chatOptions.getClass().getName());\n}","typeGuard":"static ToolCallingChatOptions asToolCalling(ChatOptions options) {\n    return options instanceof ToolCallingChatOptions tco ? tco\n        : ToolCallingChatOptions.builder().model(options.getModel()).temperature(options.getTemperature()).build();\n}","tryCatchPattern":null,"preventionTips":["Build options with ToolCallingChatOptions.builder(), never plain ChatOptions.builder()","Assert the instanceof in your own agent-setup code","Re-check provider options classes after Spring AI version upgrades","Copy custom fields manually when bridging foreign options objects"],"tags":["chat-options","type-mismatch","tool-callbacks","config"],"backgroundTag":"type-mismatch","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}