{"record":{"id":"59b14d8736aa45eb","repo":"alibaba/spring-ai-alibaba","slug":"shell-session-not-initialized-call-initialize-b","errorCode":null,"errorMessage":"Shell session not initialized. Call initialize() before executeCommand() or ensure lifecycle management (e.g., ShellToolAgentHook) is installed.","messagePattern":"Shell session not initialized\\. Call initialize\\(\\) before executeCommand\\(\\) or ensure lifecycle management \\(e\\.g\\., ShellToolAgentHook\\) is installed\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/ShellSessionManager.java","lineNumber":270,"sourceCode":"\t */\n\tpublic CommandResult executeCommand(String command, RunnableConfig config) {\n\t\tShellSession session = (ShellSession) config.context().get(SESSION_INSTANCE_CONTEXT_KEY);\n\t\tif (session == null) {\n\t\t\t// Try to recover from global registry using threadId\n\t\t\tsession = recoverSessionFromRegistry(config);\n\t\t\tif (session == null) {\n\t\t\t\t// Only auto-initialize in the HITL recovery case (threadId present).\n\t\t\t\t// For truly uninitialized usage (no threadId), preserve the previous\n\t\t\t\t// behavior and fail fast rather than starting a new shell process\n\t\t\t\t// without lifecycle management.\n\t\t\t\tif (config.threadId().isPresent()) {\n\t\t\t\t\tlog.warn(\"Shell session not found in context or registry for threadId {}. \" +\n\t\t\t\t\t\t\t\"Creating new session for HITL recovery.\", config.threadId().get());\n\t\t\t\t\tinitialize(config);\n\t\t\t\t\tsession = (ShellSession) config.context().get(SESSION_INSTANCE_CONTEXT_KEY);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\t\"Shell session not initialized. Call initialize() before executeCommand() \" +\n\t\t\t\t\t\t\t\t\t\"or ensure lifecycle management (e.g., ShellToolAgentHook) is installed.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlog.info(\"Executing shell command: {}\", command);\n\t\tCommandResult result = session.execute(command, commandTimeout, maxOutputLines, maxOutputBytes);\n\n\t\t// Apply redactions and track matches\n\t\tString output = result.getOutput();\n\t\tMap<String, List<String>> allMatches = new HashMap<>();\n\n\t\tfor (RedactionRule rule : redactionRules) {\n\t\t\tRedactionResult redactionResult = rule.applyWithMatches(output);\n\t\t\toutput = redactionResult.getRedactedContent();\n\t\t\tif (!redactionResult.getMatches().isEmpty()) {\n\t\t\t\tallMatches.computeIfAbsent(rule.getPiiType(), k -> new ArrayList<>())","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/ShellSessionManager.java#L252-L288","documentation":"executeCommand() requires an initialized shell session, looked up in the run context and the global registry. If neither holds a session for the threadId and there is no saved config to recover from (HITL resume scenario), it throws this IllegalStateException telling you to call initialize() first or install lifecycle management. It prevents executing commands on a session that was never created.","triggerScenarios":"Calling ShellTool/executeCommand() on a ShellSessionManager whose initialize() was never called for the current threadId, without a ShellToolAgentHook (or equivalent AgentHook) installed to auto-initialize, and with no context-stored session or registry entry to recover.","commonSituations":"Using ShellTool directly in a hand-rolled agent without the lifecycle hook; running the tool after the context was reset/cleared so the session key vanished; calling the tool from a new thread with a fresh threadId that never went through beforeAgent.","solutions":["Install the lifecycle hook (e.g. ShellToolAgentHook) so beforeAgent automatically calls initialize() before the tool runs.","Explicitly call ShellSessionManager.initialize(config) before the first executeCommand() call.","Verify the same context (holding SESSION_INSTANCE_CONTEXT_KEY) and threadId used at initialize() are passed to the tool call.","Catch IllegalStateException and recover by calling initialize() then retrying the command once."],"exampleFix":"// before\nShellTool tool = new ShellTool(manager);\nagent.run(\"run tests\"); // manager.initialize() never called -> IllegalStateException\n\n// after\nmanager.initialize(config); // or register ShellToolAgentHook on the agent\nShellTool tool = new ShellTool(manager);\nagent.run(\"run tests\");","handlingStrategy":"try-catch","validationCode":"// Before calling the tool, confirm a session exists:\nObject session = config.context().get(\"SHELL_SESSION_KEY\");\nif (session == null) manager.initialize(config); // ensure initialized before executeCommand","typeGuard":"boolean hasSession(ShellSessionManager mgr, RunConfig cfg) {\n    return cfg.context().get(\"SHELL_SESSION_KEY\") != null;\n}","tryCatchPattern":"try {\n    result = shellTool.execute(request, toolCtx);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"not initialized\")) {\n        manager.initialize(config); // recover, then retry once\n    }\n}","preventionTips":["Install ShellToolAgentHook (or equivalent lifecycle hook) so initialize() runs in beforeAgent automatically.","Never call executeCommand() on a fresh manager without a prior initialize().","Reuse the same context and threadId between initialize() and tool execution.","In tests, call initialize() in a @BeforeEach setup method."],"tags":["shell","lifecycle","initialization","illegal-state"],"backgroundTag":"invalid-state-transition","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"}