can1357/oh-my-pi · error · ToolError

Async job manager unavailable for this session.

Error message

Async job manager unavailable for this session.

What it means

When the caller explicitly requests async=true, the tool requires session.asyncJobManager to register the background job, deliver progress, and support later job-control calls. If the manager is absent the request cannot be honored and the tool throws immediately, before #startManagedBashJob runs. This is the explicit-async counterpart of the same guard inside #startManagedBashJob.

Source

Thrown at packages/coding-agent/src/tools/bash.ts:1013

			throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
		}

		// A timeout of 0 is an explicit long-running-command contract: the user
		// must still cancel the call or job, but OMP does not impose a deadline.
		const requestedTimeoutSec = rawTimeout;
		const timeoutDisabled = requestedTimeoutSec === 0;
		const maxTimeout = this.session.settings.get("tools.maxTimeout");
		const timeoutSec = timeoutDisabled ? undefined : clampTimeout("bash", requestedTimeoutSec, maxTimeout);
		const timeoutMs = timeoutSec === undefined ? undefined : timeoutSec * 1000;
		const pendingNotices: string[] = [];
		if (timeoutSec !== undefined) {
			const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec, maxTimeout);
			if (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);
		}

		if (asyncRequested) {
			if (!this.session.asyncJobManager) {
				throw new ToolError("Async job manager unavailable for this session.");
			}
			const job = this.#startManagedBashJob({
				command,
				commandCwd,
				timeoutMs,
				timeoutSec,
				requestedTimeoutSec,
				notices: pendingNotices,

				resolvedEnv,
				onUpdate,
				forwardUpdates: false,
			});
			return this.#buildBackgroundStartResult(job.jobId, "", timeoutSec, {
				requestedTimeoutSec,
				notices: pendingNotices,
			});
		}

View on GitHub (pinned to 9690622007)

Solutions

  1. Attach an AsyncJobManager to the session (session.asyncJobManager = new AsyncJobManager()) before making async bash calls.
  2. Run the command synchronously (omit async) if the host cannot support job management.
  3. Gate the feature in your embedding layer: check session.asyncJobManager first and disable background prompts when it's missing.

Example fix

// before: stub session missing the manager
const session = baseSession; // asyncJobManager: undefined
await bash.execute(id, { command: "make all", async: true });

// after: supply the manager or run foreground
session.asyncJobManager = new AsyncJobManager();
await bash.execute(id, { command: "make all", async: true });
Defensive patterns

Strategy: validation

Validate before calling

if (asyncRequested && !session.asyncJobManager) {
  asyncRequested = false; // degrade to foreground, or surface a config error
}
await bash.execute(id, { command, async: asyncRequested });

Try / catch

try {
  await bash.execute(id, { command, async: true });
} catch (e) {
  if (e instanceof ToolError && e.message.includes("job manager unavailable")) {
    return bash.execute(id, { command });
  }
  throw e;
}

Prevention

When it happens

Trigger: bash tool call with async=true on a session whose asyncJobManager was never attached (minimal SDK session, alternative host, or a session type without background-job support).

Common situations: Embedding the coding agent in an app with a hand-rolled session object; running the tool against a session created by an older integration layer; test harnesses stubbing the session incompletely.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/23e5cfc50b184dbf. Report an issue: GitHub.