{"record":{"id":"8f8b726a6a319421","repo":"alibaba/spring-ai-alibaba","slug":"failed-to-extract-shelltool2-from-tool","errorCode":null,"errorMessage":"Failed to extract ShellTool2 from tool: {}","messagePattern":"Failed to extract ShellTool2 from tool: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/shelltool/ShellToolAgentHook.java","lineNumber":143,"sourceCode":"\tpublic void injectTool(ToolCallback toolCallback) {\n\t\tif (shellTool2 != null) {\n\t\t\t// Tool already injected\n\t\t\treturn;\n\t\t}\n\n\t\tlog.info(\"ShellToolAgentHook: Processing tool callback for shell tool extraction\");\n\n\t\ttry {\n\t\t\t// Extract ShellTool2\n\t\t\tShellTool2 extractedShellTool2 = extractShellTool2(toolCallback);\n\t\t\tif (extractedShellTool2 != null) {\n\t\t\t\tthis.shellTool2 = extractedShellTool2;\n\t\t\t\tlog.info(\"Successfully extracted and injected ShellTool2 from tool: {}\",\n\t\t\t\t\t\ttoolCallback.getToolDefinition().name());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlog.warn(\"Failed to extract ShellTool2 from tool: {}\",\n\t\t\t\t\ttoolCallback.getToolDefinition().name());\n\t\t} catch (Exception e) {\n\t\t\tlog.error(\"Error extracting ShellTool2 from tool callback\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Extract ShellTool2 instance from ToolCallback using reflection.\n\t * Supports MethodToolCallback wrapping ShellTool2 (with @Tool annotation).\n\t */\n\tprivate ShellTool2 extractShellTool2(ToolCallback toolCallback) {\n\t\ttry {\n\t\t\tClass<?> clazz = toolCallback.getClass();\n\n\t\t\t// Look for 'toolObject' field in MethodToolCallback\n\t\t\twhile (clazz != null) {\n\t\t\t\ttry {\n\t\t\t\t\tField toolObjectField = clazz.getDeclaredField(\"toolObject\");","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/shelltool/ShellToolAgentHook.java#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Register the actual ShellTool2 (or ShellTool2.builder()...buildToolCallback()) so extraction succeeds","If using a wrapper, expose the underlying ShellTool2 where the hook expects it, or inject ShellTool2 directly into the hook builder","Log/inspect the callback's delegate chain and compare against the supported types in ShellToolAgentHook.injectTool","Check for dependency version mismatch between spring-ai-alibaba-agent-framework modules"],"exampleFix":"// before\nagentBuilder.tools(myCustomShellWrapperCallback); // hook cannot extract ShellTool2\n// after\nagentBuilder.tools(shellTool2)\n    .hooks(ShellToolAgentHook.builder().shellTool2(shellTool2).build());","handlingStrategy":"type-guard","validationCode":"Objects.requireNonNull(toolCallback, \"toolCallback must not be null\");\nif (!(toolCallback instanceof FunctionToolCallback)) { log.warn(\"injectTool expects FunctionToolCallback wrapping ShellTool2\"); }","typeGuard":"static ShellTool2 extractIfShellTool(ToolCallback cb) {\n    if (cb instanceof FunctionToolCallback fcb && fcb.getToolDefinition() != null) {\n        Object delegate = fcb.getToolDefinition();\n        if (delegate instanceof ShellTool2 st2) return st2;\n    }\n    return null;\n}","tryCatchPattern":"try { injectTool(cb); } catch (Exception e) { log.error(\"ShellTool2 injection failed for {}\", cb, e); }","preventionTips":["Pass ShellTool2 itself (or its framework-provided callback) rather than custom wrappers","Keep agent-framework module versions aligned so delegate types match","Unit-test hook wiring: injectTool then assert shellTool2 != null"],"tags":["shell","shelltool","type-extraction","tool-callback"],"backgroundTag":"missing-dependency","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"}