alibaba/spring-ai-alibaba · error · IllegalStateException

Shell session not initialized. Call initialize() before exec

Error message

Shell session not initialized. Call initialize() before executeCommand() or ensure lifecycle management (e.g., ShellToolAgentHook) is installed.

What it means

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.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/ShellSessionManager.java:270

	 */
	public CommandResult executeCommand(String command, RunnableConfig config) {
		ShellSession session = (ShellSession) config.context().get(SESSION_INSTANCE_CONTEXT_KEY);
		if (session == null) {
			// Try to recover from global registry using threadId
			session = recoverSessionFromRegistry(config);
			if (session == null) {
				// Only auto-initialize in the HITL recovery case (threadId present).
				// For truly uninitialized usage (no threadId), preserve the previous
				// behavior and fail fast rather than starting a new shell process
				// without lifecycle management.
				if (config.threadId().isPresent()) {
					log.warn("Shell session not found in context or registry for threadId {}. " +
							"Creating new session for HITL recovery.", config.threadId().get());
					initialize(config);
					session = (ShellSession) config.context().get(SESSION_INSTANCE_CONTEXT_KEY);
				}
				else {
					throw new IllegalStateException(
							"Shell session not initialized. Call initialize() before executeCommand() " +
									"or ensure lifecycle management (e.g., ShellToolAgentHook) is installed.");
				}
			}
		}

		log.info("Executing shell command: {}", command);
		CommandResult result = session.execute(command, commandTimeout, maxOutputLines, maxOutputBytes);

		// Apply redactions and track matches
		String output = result.getOutput();
		Map<String, List<String>> allMatches = new HashMap<>();

		for (RedactionRule rule : redactionRules) {
			RedactionResult redactionResult = rule.applyWithMatches(output);
			output = redactionResult.getRedactedContent();
			if (!redactionResult.getMatches().isEmpty()) {
				allMatches.computeIfAbsent(rule.getPiiType(), k -> new ArrayList<>())

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Install the lifecycle hook (e.g. ShellToolAgentHook) so beforeAgent automatically calls initialize() before the tool runs.
  2. Explicitly call ShellSessionManager.initialize(config) before the first executeCommand() call.
  3. Verify the same context (holding SESSION_INSTANCE_CONTEXT_KEY) and threadId used at initialize() are passed to the tool call.
  4. Catch IllegalStateException and recover by calling initialize() then retrying the command once.

Example fix

// before
ShellTool tool = new ShellTool(manager);
agent.run("run tests"); // manager.initialize() never called -> IllegalStateException

// after
manager.initialize(config); // or register ShellToolAgentHook on the agent
ShellTool tool = new ShellTool(manager);
agent.run("run tests");
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling the tool, confirm a session exists:
Object session = config.context().get("SHELL_SESSION_KEY");
if (session == null) manager.initialize(config); // ensure initialized before executeCommand

Type guard

boolean hasSession(ShellSessionManager mgr, RunConfig cfg) {
    return cfg.context().get("SHELL_SESSION_KEY") != null;
}

Try / catch

try {
    result = shellTool.execute(request, toolCtx);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("not initialized")) {
        manager.initialize(config); // recover, then retry once
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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