{"record":{"id":"477f55a5ecf23986","repo":"alibaba/spring-ai-alibaba","slug":"either-chatclient-or-model-must-be-provided","errorCode":null,"errorMessage":"Either chatClient or model must be provided","messagePattern":"Either chatClient or model must be provided","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/DefaultBuilder.java","lineNumber":64,"sourceCode":"\npublic class DefaultBuilder extends Builder {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DefaultBuilder.class);\n\n\tpublic static final String POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING\n\t\t\t\t= \"LLM may have adapted the tool name '{}', especially if the name was truncated due to length limits. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator\";\n\n\t@Override\n\tpublic ReactAgent build() {\n\n\t\t// Validate name is not empty\n\t\tif (!StringUtils.hasText(this.name)) {\n\t\t\tthrow new IllegalArgumentException(\"Agent name must not be empty\");\n\t\t}\n\n\t\t// Validate either chatClient or model is provided\n\t\tif (chatClient == null && model == null) {\n\t\t\tthrow new IllegalArgumentException(\"Either chatClient or model must be provided\");\n\t\t}\n\n\t\t// Get source options from ChatClient (when provided) or from ChatModel (when building from model).\n\t\tChatOptions sourceOptions = (chatClient != null)\n\t\t\t\t? getChatClientDefaultOptions(chatClient)\n\t\t\t\t: getChatModelDefaultOptions(model);\n\t\tChatOptions effectiveOptions = mergeSourceOptionsWithAgentOptions(sourceOptions, this.chatOptions);\n\n\t\tif (chatClient == null) {\n\t\t\tChatClient.Builder clientBuilder = ChatClient.builder(model,\n\t\t\t\t\tthis.observationRegistry == null ? ObservationRegistry.NOOP : this.observationRegistry,\n\t\t\t\t\tthis.customObservationConvention, this.advisorObservationConvention);\n\t\t\tif (effectiveOptions != null) {\n\t\t\t\tclientBuilder.defaultOptions(effectiveOptions);\n\t\t\t}\n\t\t\tchatClient = clientBuilder.build();\n\t\t} else {\n\t\t\tchatClient = chatClient.mutate().defaultOptions(effectiveOptions).build();","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/DefaultBuilder.java#L46-L82","documentation":"DefaultBuilder.build() requires that at least one of chatClient or model is set; if both are null it throws IllegalArgumentException. A ReactAgent needs an underlying model (directly or through a ChatClient) to run the ReAct loop.","triggerScenarios":"Calling ReactAgent.builder().name(\"x\").build() without .model(ChatModel) or .chatClient(ChatClient).","commonSituations":"Forgetting to wire the ChatModel bean (missing DashScope/OpenAI starter config or API key so the bean is absent), or building agents programmatically in tests without stubbing a model.","solutions":["Add .model(chatModel) (e.g. DashScopeChatModel) to the builder.","Or add .chatClient(chatClient) built with ChatClient.builder(chatModel).build().","Ensure the model bean exists: configure the DashScope/OpenAI starter and API key so the ChatModel bean is injected.","In tests, pass a stub ChatModel implementation."],"exampleFix":"// before\nReactAgent.builder().name(\"bot\").build();\n// after\nReactAgent.builder().name(\"bot\").model(chatModel).build();","handlingStrategy":"validation","validationCode":"if (chatClient == null && chatModel == null) throw new IllegalArgumentException(\"Provide either a ChatClient or a ChatModel to the ReactAgent builder\");","typeGuard":null,"tryCatchPattern":"try { agent = builder.build(); } catch (IllegalArgumentException e) { log.error(\"Agent '{}': {}\", builderName, e.getMessage()); throw e; }","preventionTips":["Wire the ChatModel bean via the DashScope/OpenAI starter and verify the app context starts.","In a factory method, require a ChatModel parameter so the compiler enforces presence.","For tests, provide a stub ChatModel by default."],"tags":["builder-validation","missing-dependency","illegal-argument"],"backgroundTag":"missing-required-argument","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"}