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
- 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.
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
- Serialize browser tasks in persistent/existing mode.
- Always pair acquire() with release() in a finally block.
- Switch to 'isolated' mode when you need parallelism.
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
- Maximum number of parallel browser instances (${BrowserManag
- Domain not allowed: The requested domain is not in the allow
- Invalid domain in allowedDomains: ${domain}
- Workspace path ${resolvedPath} is outside the allowed root d
- Workspace path ${resolvedPath} does not exist
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/81d1bd5365e6dfe7.
Report an issue: GitHub.