{"record":{"id":"f3eb29b7680bbd78","repo":"microsoft/playwright","slug":"browser-is-already-in-use-for-userdatadir-use","errorCode":null,"errorMessage":"Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser","messagePattern":"Browser is already in use for (.+?), use --isolated to run multiple instances of the same browser","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/tools/mcp/browserFactory.ts","lineNumber":164,"sourceCode":"\n  const playwrightObject = playwright as Playwright;\n  // Use connectToBrowser instead of playwright[browserName].connect because we don't have browserName.\n  const browser = await connectToBrowser(playwrightObject, remoteOptions);\n  browser._connectToBrowserType(playwrightObject[browser._browserName], {}, undefined);\n  // A browser started via `launchServer` exposes no contexts until one is\n  // created, so create one when attaching to such a server.\n  if (!browser.contexts().length)\n    await browser.newContext(config.browser.contextOptions);\n  return { browser, browserInfo: browserInfo(browser, config), canBind: false, ownership: 'attached' };\n}\n\nasync function createPersistentBrowser(config: FullConfig, clientInfo: ClientInfo): Promise<playwrightTypes.Browser> {\n  testDebug('create browser (persistent)');\n  const userDataDir = config.browser.userDataDir ?? await createUserDataDir(config, clientInfo);\n  const tracesDir = await computeTracesDir(config, clientInfo);\n\n  if (await isProfileLocked5Times(userDataDir))\n    throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);\n\n  const browserType = playwright[config.browser.browserName];\n  const configIgnoreDefaultArgs = config.browser.launchOptions?.ignoreDefaultArgs;\n  const launchOptions: playwrightTypes.LaunchOptions & playwrightTypes.BrowserContextOptions = {\n    tracesDir,\n    ...config.browser.launchOptions,\n    ...config.browser.contextOptions,\n    handleSIGINT: false,\n    handleSIGTERM: false,\n    ignoreDefaultArgs: configIgnoreDefaultArgs === true\n      ? true\n      : [\n        '--disable-extensions',\n        ...Array.isArray(configIgnoreDefaultArgs) ? configIgnoreDefaultArgs : [],\n      ],\n  };\n  try {\n    const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/tools/mcp/browserFactory.ts#L146-L182","documentation":"Thrown by `createPersistentBrowser` after `isProfileLocked5Times` returns true — meaning the profile's lock file (SingletonLock on Unix, lockfile on Windows) was held by a live process for ~5 seconds of retries. Persistent mode requires exclusive access to the userDataDir, so a locked profile aborts launch. The message directs the user to `--isolated`.","triggerScenarios":"Launching a persistent browser (non-isolated) against a userDataDir already in use by another running browser instance (another MCP/CLI session, a manually-opened Chrome with the same profile, or a prior crashed process whose lock is held). The pre-launch lock check in `isProfileLocked5Times` polls 5x at 1s intervals.","commonSituations":"Two MCP clients using the same workspace/profile concurrently; a user has Chrome open with that profile; a previous browser process did not clean up its SingletonLock; running on CI where a parallel job shares the profiles dir.","solutions":["Close the other browser instance using that profile, then retry.","Use `--isolated` so each session gets its own ephemeral context (no shared userDataDir).","If the lock is stale (process died), remove the SingletonLock/lockfile under the userDataDir and retry.","Give each concurrent session a distinct `--user-data-dir` or `--profile`."],"exampleFix":"# before\nplaywright mcp --user-data-dir ~/.cache/mcp-profile\n# (second concurrent invocation fails)\n# after\nplaywright mcp --isolated","handlingStrategy":"validation","validationCode":"import { isProfileLocked } from '@tools/mcp/browserFactory';\nasync function assertProfileFree(userDataDir: string) {\n  if (await isProfileLocked(userDataDir))\n    throw new Error(`Profile ${userDataDir} is locked by another process; use --isolated.`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await launchPersistent(opts);\n} catch (e) {\n  if (/Browser is already in use/.test((e as Error).message)) {\n    opts.isolated = true; await launchPersistent(opts); // fall back to isolated\n  } else throw e;\n}","preventionTips":["Use --isolated for concurrent sessions.","Give each session its own --user-data-dir/--profile.","Close other browser instances using a shared profile before launching."],"tags":["browser-launch","persistent-profile","lock","isolated-mode","concurrency"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}