google-gemini/gemini-cli · error · Error

Cannot launch a concurrent browser agent in "${sessionMode}"

Error message

Cannot launch a concurrent browser agent in "${sessionMode}" session mode. The browser instance is already in use by another task. Please run browser tasks sequentially, or switch to "isolated" session mode for concurrent browser usage.

What it means

Thrown by BrowserManager.getInstance when an existing instance for the same sessionMode:profilePath key has inUse===true and the configured sessionMode is 'persistent' or 'existing'. Chrome refuses two processes on the same user-data directory, so the manager forbids concurrency in those modes rather than producing a 'profile locked' crash.

Source

Thrown at packages/core/src/agents/browser/browserManager.ts:159

   *   caller to run browser tasks sequentially.
   * - **isolated mode**: Parallel instances are allowed up to
   *   MAX_PARALLEL_INSTANCES. Each isolated instance gets its own temp profile.
   */
  static getInstance(config: Config): BrowserManager {
    const key = BrowserManager.getInstanceKey(config);
    const sessionMode =
      config.getBrowserAgentConfig().customConfig.sessionMode ?? 'persistent';
    let instance = BrowserManager.instances.get(key);
    if (!instance) {
      instance = new BrowserManager(config);
      BrowserManager.instances.set(key, instance);
      debugLogger.log(`Created new BrowserManager singleton (key: ${key})`);
    } else if (instance.inUse) {
      // Persistent and existing modes share a browser profile directory.
      // Chrome prevents multiple instances from using the same profile, so
      // concurrent usage would cause "profile locked" errors.
      if (sessionMode === 'persistent' || sessionMode === 'existing') {
        throw new Error(
          `Cannot launch a concurrent browser agent in "${sessionMode}" session mode. ` +
            `The browser instance is already in use by another task. ` +
            `Please run browser tasks sequentially, or switch to "isolated" session mode for concurrent browser usage.`,
        );
      }

      // Isolated mode: allow parallel instances up to the limit.
      let inUseCount = 1; // primary is already in-use
      let suffix = 1;
      let parallelKey = `${key}:${suffix}`;
      let parallel = BrowserManager.instances.get(parallelKey);
      while (parallel?.inUse) {
        inUseCount++;
        if (inUseCount >= BrowserManager.MAX_PARALLEL_INSTANCES) {
          throw new Error(
            `Maximum number of parallel browser instances (${BrowserManager.MAX_PARALLEL_INSTANCES}) reached. ` +
              `Please wait for an existing browser task to complete before starting a new one.`,
          );

View on GitHub (pinned to 5024443c72)

Solutions

  1. Run browser tasks sequentially so each finishes (and release() runs) before the next starts.
  2. Set customConfig.sessionMode to 'isolated' to allow parallel instances with separate temp profiles.
  3. Ensure every browser invocation wraps work in try/finally so acquire()/release() stay balanced even on errors.
  4. If a stale inUse flag is suspected (crashed prior run), call BrowserManager.resetAll() or restart the CLI to clear the cache.

Example fix

// before
browserAgent: { customConfig: { sessionMode: 'persistent' } }
// two parallel browser_agent calls -> error

// after: allow concurrency
browserAgent: { customConfig: { sessionMode: 'isolated' } }
Defensive patterns

Strategy: validation

Validate before calling

// Before fanning out, check the singleton state.
import { BrowserManager } from './browserManager.js';
function canLaunchBrowser(config) {
  const inst = BrowserManager.instances?.get(BrowserManager.getInstanceKey(config));
  const mode = config.getBrowserAgentConfig().customConfig.sessionMode ?? 'persistent';
  if ((mode === 'persistent' || mode === 'existing') && inst?.isAcquired())
    return false;
  return true;
}

Type guard

function isPersistentOrExisting(mode) {
  return mode === 'persistent' || mode === 'existing';
}

Try / catch

try {
  const bm = BrowserManager.getInstance(config);
  bm.acquire();
  try { /* ...browser work... */ } finally { bm.release(); }
} catch (e) {
  if (e instanceof Error && /concurrent browser agent/.test(e.message)) {
    // queue the task, or switch config.sessionMode to 'isolated'
  }
  throw e;
}

Prevention

When it happens

Trigger: Two near-simultaneous browser_agent calls (or a browser subagent running while the parent also drives the browser) while sessionMode is 'persistent' (default) or 'existing'. The first call acquired() the instance; the second sees inUse===true and this branch throws.

Common situations: A subagent and the main agent both invoke browser_agent; a fan-out of parallel tool calls each requesting browser work; a previous browser task did not release() (leaked acquire) due to an error path; user intentionally wants concurrency but left the default persistent mode.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/81d1bd5365e6dfe7. Report an issue: GitHub.