{"record":{"id":"a93b7fd38cc18fa4","repo":"alibaba/spring-ai-alibaba","slug":"toolcallbackresolver-is-null-cannot-resolve-tool","errorCode":null,"errorMessage":"ToolCallbackResolver is null; cannot resolve tool name: ","messagePattern":"ToolCallbackResolver is null; cannot resolve tool name: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/DefaultBuilder.java","lineNumber":258,"sourceCode":"\t\t}\n\t\t\n\t\tif (CollectionUtils.isNotEmpty(toolCallbackProviders)) {\n\t\t\tfor (var provider : toolCallbackProviders) {\n\t\t\t\tregularTools.addAll(List.of(provider.getToolCallbacks()));\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (CollectionUtils.isNotEmpty(toolNames)) {\n\t\t\tfor (String toolName : toolNames) {\n\t\t\t\t// Skip the tool if it is already present in the request toolCallbacks.\n\t\t\t\t// That might happen if a tool is defined in the options\n\t\t\t\t// both as a ToolCallback and as a tool name.\n\t\t\t\tif (regularTools.stream().anyMatch(tool -> tool.getToolDefinition().name().equals(toolName))) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (this.resolver == null) {\n\t\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\t\"ToolCallbackResolver is null; cannot resolve tool name: \" + toolName);\n\t\t\t\t}\n\t\t\t\tToolCallback toolCallback = this.resolver.resolve(toolName);\n\t\t\t\tif (toolCallback == null) {\n\t\t\t\t\tlogger.warn(POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING, toolName);\n\t\t\t\t\tthrow new IllegalStateException(\"No ToolCallback found for tool name: \" + toolName);\n\t\t\t\t}\n\t\t\t\tregularTools.add(toolCallback);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// If regularTools is empty and resolver is provided, try to extract tools from resolver\n\t\tif (regularTools.isEmpty() && this.resolver != null) {\n\t\t\t// Check if resolver also implements ToolCallbackProvider\n\t\t\tif (this.resolver instanceof ToolCallbackProvider provider) {\n\t\t\t\tToolCallback[] resolverTools = provider.getToolCallbacks();\n\t\t\t\tif (resolverTools != null && resolverTools.length > 0) {\n\t\t\t\t\tregularTools.addAll(List.of(resolverTools));","sourceCodeStart":240,"sourceCodeEnd":276,"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#L240-L276","documentation":"DefaultBuilder.gatherLocalTools (reached via allTools) resolves tools registered only by name through the configured ToolCallbackResolver. If the resolver reference is null it throws IllegalStateException, since tool names cannot be turned into ToolCallback instances without it.","triggerScenarios":"Building a ReactAgent where tools were supplied as String tool names (e.g. .tools(\"myTool\")) but no ToolCallbackResolver was set on the builder.","commonSituations":"Using tool-name registration in a plain application without Spring's tool resolution wiring; forgetting .toolCallbackResolver(...) on the builder; constructing DefaultBuilder manually outside Spring context.","solutions":["Set the resolver on the builder: .toolCallbackResolver(resolver) (e.g. the Spring ToolCallbackResolver bean).","Register the tools as ToolCallback/MethodToolCallback objects instead of bare names.","Ensure the Spring context is fully started so the resolver bean is available before building the agent."],"exampleFix":"// before\nReactAgent.builder().name(\"a\").model(model).tools(\"getTime\").build();\n// after\nReactAgent.builder().name(\"a\").model(model).tools(\"getTime\").toolCallbackResolver(resolver).build();","handlingStrategy":"type-guard","validationCode":"if (resolver == null && !Collections.disjoint(toolNames, toolNames)) throw new IllegalStateException(\"Tool names registered but no ToolCallbackResolver set\");","typeGuard":"boolean canResolveByName(ToolCallbackResolver r, List<String> names) { return r != null || names == null || names.isEmpty(); }","tryCatchPattern":"try { agent = builder.build(); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"ToolCallbackResolver is null\")) { throw new IllegalStateException(\"Set .toolCallbackResolver(...) when registering tools by name\", e); } throw e; }","preventionTips":["Whenever passing tool name strings, also set .toolCallbackResolver(...).","Prefer registering ToolCallback objects over names when no Spring resolver exists.","Assert resolver presence in a builder factory."],"tags":["tool-resolution","null-reference","illegal-state"],"backgroundTag":"null-argument","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}