{"record":{"id":"43dbbca570784f7d","repo":"alibaba/spring-ai-alibaba","slug":"shell-session-not-found-in-context-or-registry-for","errorCode":null,"errorMessage":"Shell session not found in context or registry for threadId {}. Creating new session for HITL recovery.","messagePattern":"Shell session not found in context or registry for threadId (.+?)\\. Creating new session for HITL recovery\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/ShellSessionManager.java","lineNumber":264,"sourceCode":"\t * where a fresh {@link RunnableConfig} with an empty context is used), the method will:\n\t * <ol>\n\t *   <li>First attempt to recover the existing session from the global registry using threadId</li>\n\t *   <li>If not found in registry, create a new session as fallback</li>\n\t * </ol>\n\t * This ensures shell state (working directory, environment variables) is preserved across HITL interrupts.</p>\n\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<>();","sourceCodeStart":246,"sourceCodeEnd":282,"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#L246-L282","documentation":"ShellSessionManager.executeCommand found no ShellSession stored in the tool context or session registry for the given threadId. When a threadId is present it logs this warning and auto-initializes a new session so that human-in-the-loop (HITL) resumed executions can continue; when no threadId is present it fails fast with IllegalStateException instead, since no lifecycle management would exist for the new process.","triggerScenarios":"Calling executeCommand (directly or via the shell tool 'result' path) with a config whose threadId is set, but the process restarted or the context was rebuilt so the previously initialized session is gone from context/registry.","commonSituations":"Application restart between tool call and human approval (HITL flow), context map not propagated through graph checkpointing, calling the tool from a different thread than the one that initialized the session.","solutions":["No action strictly needed: the manager auto-recovers by calling initialize(config) for HITL; verify the new session works and prior working directory/env expectations are re-applied.","If sessions must survive restarts, persist session metadata (working dir, env) in graph state/checkpoints and re-initialize deterministically.","Ensure the same threadId is used on resume so the registry lookup succeeds and recovery is not needed.","If you hit this with NO threadId, that is a different failure (IllegalStateException): pass a threadId in the tool config."],"exampleFix":"// before\nString result = shellTool.executeCommand(\"ls\", null);\n// after\nString result = shellTool.executeCommand(\"ls\", \"my-thread-123\");","handlingStrategy":"validation","validationCode":"if (threadId == null || threadId.isBlank()) throw new IllegalArgumentException(\"threadId required for shell tool usage with HITL resume\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass a stable threadId in the shell tool config so sessions can be recovered.","Persist session-relevant state (cwd, env) in graph checkpoints if restarts are expected.","Log session lifecycle events to correlate recovery warnings."],"tags":["shell","session","hitl","recovery"],"backgroundTag":"resource-not-found","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"}