{"record":{"id":"81d1bd5365e6dfe7","repo":"google-gemini/gemini-cli","slug":"cannot-launch-a-concurrent-browser-agent-in-ses","errorCode":null,"errorMessage":"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.","messagePattern":"Cannot launch a concurrent browser agent in \"(.+?)\" 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\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/browser/browserManager.ts","lineNumber":159,"sourceCode":"   *   caller to run browser tasks sequentially.\n   * - **isolated mode**: Parallel instances are allowed up to\n   *   MAX_PARALLEL_INSTANCES. Each isolated instance gets its own temp profile.\n   */\n  static getInstance(config: Config): BrowserManager {\n    const key = BrowserManager.getInstanceKey(config);\n    const sessionMode =\n      config.getBrowserAgentConfig().customConfig.sessionMode ?? 'persistent';\n    let instance = BrowserManager.instances.get(key);\n    if (!instance) {\n      instance = new BrowserManager(config);\n      BrowserManager.instances.set(key, instance);\n      debugLogger.log(`Created new BrowserManager singleton (key: ${key})`);\n    } else if (instance.inUse) {\n      // Persistent and existing modes share a browser profile directory.\n      // Chrome prevents multiple instances from using the same profile, so\n      // concurrent usage would cause \"profile locked\" errors.\n      if (sessionMode === 'persistent' || sessionMode === 'existing') {\n        throw new Error(\n          `Cannot launch a concurrent browser agent in \"${sessionMode}\" session mode. ` +\n            `The browser instance is already in use by another task. ` +\n            `Please run browser tasks sequentially, or switch to \"isolated\" session mode for concurrent browser usage.`,\n        );\n      }\n\n      // Isolated mode: allow parallel instances up to the limit.\n      let inUseCount = 1; // primary is already in-use\n      let suffix = 1;\n      let parallelKey = `${key}:${suffix}`;\n      let parallel = BrowserManager.instances.get(parallelKey);\n      while (parallel?.inUse) {\n        inUseCount++;\n        if (inUseCount >= BrowserManager.MAX_PARALLEL_INSTANCES) {\n          throw new Error(\n            `Maximum number of parallel browser instances (${BrowserManager.MAX_PARALLEL_INSTANCES}) reached. ` +\n              `Please wait for an existing browser task to complete before starting a new one.`,\n          );","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/agents/browser/browserManager.ts#L141-L177","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run browser tasks sequentially so each finishes (and release() runs) before the next starts.","Set customConfig.sessionMode to 'isolated' to allow parallel instances with separate temp profiles.","Ensure every browser invocation wraps work in try/finally so acquire()/release() stay balanced even on errors.","If a stale inUse flag is suspected (crashed prior run), call BrowserManager.resetAll() or restart the CLI to clear the cache."],"exampleFix":"// before\nbrowserAgent: { customConfig: { sessionMode: 'persistent' } }\n// two parallel browser_agent calls -> error\n\n// after: allow concurrency\nbrowserAgent: { customConfig: { sessionMode: 'isolated' } }","handlingStrategy":"validation","validationCode":"// Before fanning out, check the singleton state.\nimport { BrowserManager } from './browserManager.js';\nfunction canLaunchBrowser(config) {\n  const inst = BrowserManager.instances?.get(BrowserManager.getInstanceKey(config));\n  const mode = config.getBrowserAgentConfig().customConfig.sessionMode ?? 'persistent';\n  if ((mode === 'persistent' || mode === 'existing') && inst?.isAcquired())\n    return false;\n  return true;\n}","typeGuard":"function isPersistentOrExisting(mode) {\n  return mode === 'persistent' || mode === 'existing';\n}","tryCatchPattern":"try {\n  const bm = BrowserManager.getInstance(config);\n  bm.acquire();\n  try { /* ...browser work... */ } finally { bm.release(); }\n} catch (e) {\n  if (e instanceof Error && /concurrent browser agent/.test(e.message)) {\n    // queue the task, or switch config.sessionMode to 'isolated'\n  }\n  throw e;\n}","preventionTips":["Serialize browser tasks in persistent/existing mode.","Always pair acquire() with release() in a finally block.","Switch to 'isolated' mode when you need parallelism."],"tags":["browser","concurrency","config"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}