alibaba/spring-ai-alibaba · warning

Failed to extract ShellTool2 from tool

Error message

Failed to extract ShellTool2 from tool: {}

What it means

ShellToolAgentHook.injectTool() logs this warning when a registered ToolCallback cannot be unwrapped into a ShellTool2 instance. The hook accepts FunctionToolCallbacks whose delegate (or nested delegate chain) is a ShellTool2; if extraction fails, shellTool2 stays null and later hook phases log the 'No ShellTool2 injected' warnings and are skipped.

Solutions

  1. Register the actual ShellTool2 (or ShellTool2.builder()...buildToolCallback()) so extraction succeeds
  2. If using a wrapper, expose the underlying ShellTool2 where the hook expects it, or inject ShellTool2 directly into the hook builder
  3. Log/inspect the callback's delegate chain and compare against the supported types in ShellToolAgentHook.injectTool
  4. Check for dependency version mismatch between spring-ai-alibaba-agent-framework modules

Example fix

// before
agentBuilder.tools(myCustomShellWrapperCallback); // hook cannot extract ShellTool2
// after
agentBuilder.tools(shellTool2)
    .hooks(ShellToolAgentHook.builder().shellTool2(shellTool2).build());
Defensive patterns

Strategy: type-guard

Validate before calling

Objects.requireNonNull(toolCallback, "toolCallback must not be null");
if (!(toolCallback instanceof FunctionToolCallback)) { log.warn("injectTool expects FunctionToolCallback wrapping ShellTool2"); }

Type guard

static ShellTool2 extractIfShellTool(ToolCallback cb) {
    if (cb instanceof FunctionToolCallback fcb && fcb.getToolDefinition() != null) {
        Object delegate = fcb.getToolDefinition();
        if (delegate instanceof ShellTool2 st2) return st2;
    }
    return null;
}

Try / catch

try { injectTool(cb); } catch (Exception e) { log.error("ShellTool2 injection failed for {}", cb, e); }

Prevention

When it happens

Trigger: Calling injectTool(toolCallback) (typically from the agent builder wiring tools) with a callback that is not a FunctionToolCallback wrapping ShellTool2, or whose delegate is of an unexpected type, so the cast/extraction throws or returns nothing.

Common situations: Registering a custom or third-party shell tool callback instead of the framework ShellTool2; wrapping ShellTool2 in your own decorator so reflective extraction fails; version drift where the delegate class moved packages; calling injectTool with a null callback.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/8f8b726a6a319421. Report an issue: GitHub.

Appendix: source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/shelltool/ShellToolAgentHook.java:143

	public void injectTool(ToolCallback toolCallback) {
		if (shellTool2 != null) {
			// Tool already injected
			return;
		}

		log.info("ShellToolAgentHook: Processing tool callback for shell tool extraction");

		try {
			// Extract ShellTool2
			ShellTool2 extractedShellTool2 = extractShellTool2(toolCallback);
			if (extractedShellTool2 != null) {
				this.shellTool2 = extractedShellTool2;
				log.info("Successfully extracted and injected ShellTool2 from tool: {}",
						toolCallback.getToolDefinition().name());
				return;
			}

			log.warn("Failed to extract ShellTool2 from tool: {}",
					toolCallback.getToolDefinition().name());
		} catch (Exception e) {
			log.error("Error extracting ShellTool2 from tool callback", e);
		}
	}

	/**
	 * Extract ShellTool2 instance from ToolCallback using reflection.
	 * Supports MethodToolCallback wrapping ShellTool2 (with @Tool annotation).
	 */
	private ShellTool2 extractShellTool2(ToolCallback toolCallback) {
		try {
			Class<?> clazz = toolCallback.getClass();

			// Look for 'toolObject' field in MethodToolCallback
			while (clazz != null) {
				try {
					Field toolObjectField = clazz.getDeclaredField("toolObject");

View on GitHub (pinned to f82da0b50f)