{"record":{"id":"e09390ea9ec8822b","repo":"microsoft/playwright","slug":"playwright-extension-not-found-in-userdatadir","errorCode":null,"errorMessage":"Playwright Extension not found in \"${userDataDir}\". Install it from ${playwrightExtensionInstallUrl}, or set the PLAYWRIGHT_MCP_EXECUTABLE_PATH environment variable to use a browser at a custom location.","messagePattern":"Playwright Extension not found in \"(.+?)\"\\. Install it from (.+?), or set the PLAYWRIGHT_MCP_EXECUTABLE_PATH environment variable to use a browser at a custom location\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/tools/mcp/extensionContextFactory.ts","lineNumber":33,"sourceCode":" */\n\nimport debug from 'debug';\nimport { defaultUserDataDirForChannel } from '@utils/chromiumChannels';\nimport { playwright } from '../../inprocess';\nimport { findPlaywrightExtensionProfile, playwrightExtensionInstallUrl } from '../utils/extension';\nimport { CDPRelayServer } from './cdpRelay';\n\nimport type * as playwrightTypes from '../../..';\n\nconst debugLogger = debug('pw:mcp:relay');\n\nexport async function createExtensionBrowser(channel: string, executablePath: string | undefined, customUserDataDir: string | undefined, clientName: string): Promise<playwrightTypes.Browser> {\n  customUserDataDir ??= process.env.PWTEST_EXTENSION_USER_DATA_DIR;\n  // Custom executablePath may target a browser in a different filesystem (e.g. Windows chrome.exe from WSL2), so the local profile path is not meaningful.\n  const userDataDir = customUserDataDir ?? (executablePath ? undefined : defaultUserDataDirForChannel(channel));\n  const profileDirectory = userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : undefined;\n  if (!executablePath && userDataDir && !profileDirectory)\n    throw new Error(`Playwright Extension not found in \"${userDataDir}\". Install it from ${playwrightExtensionInstallUrl}, or set the PLAYWRIGHT_MCP_EXECUTABLE_PATH environment variable to use a browser at a custom location.`);\n\n  const relay = new CDPRelayServer(channel, executablePath, customUserDataDir, profileDirectory);\n  await relay.start();\n  debugLogger(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`);\n\n  try {\n    await relay.establishExtensionConnection(clientName);\n    const browser = await playwright.chromium.connectOverCDP(relay.cdpEndpoint(), { isLocal: true, timeout: 0, noDefaults: true });\n    browser.on('disconnected', () => relay.stop());\n    return browser;\n  } catch (error) {\n    relay.stop();\n    throw error;\n  }\n}\n","sourceCodeStart":15,"sourceCodeEnd":49,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts#L15-L49","documentation":"Thrown by createExtensionBrowser() when launching in extension mode: no custom executablePath was supplied, a user-data directory was resolved, but findPlaywrightExtensionProfile() could not locate the installed Playwright browser extension in that profile. The error points the user to the extension install URL or to PLAYWRIGHT_MCP_EXECUTABLE_PATH to point at a browser elsewhere. Extension mode requires the extension to be installed so a CDP relay can be established.","triggerScenarios":"Using --extension (or extension config) for the first time without having installed the Playwright browser extension into the target Chrome/Edge profile; the profile directory was reset or the extension removed; running in an environment (CI/container) where the browser profile has never had the extension added.","commonSituations":"First-time setup of extension mode; fresh OS/profile; extension auto-removed by the browser; pointing at a profile that belongs to a different channel than the one with the extension installed.","solutions":["Install the Playwright browser extension from the URL shown in the error message into the target browser profile, then retry.","Set PLAYWRIGHT_MCP_EXECUTABLE_PATH to a browser binary if you want to bypass extension discovery and point at a specific browser.","Verify the user-data-dir resolution: if you did not pass one, the code uses defaultUserDataDirForChannel(channel) — ensure that profile is the one with the extension.","Confirm the browser channel (chrome/msedge/etc.) matches the profile the extension was installed into."],"exampleFix":"// before\nplaywright mcp --extension\n// after (point at a browser explicitly)\nPLAYWRIGHT_MCP_EXECUTABLE_PATH=/usr/bin/google-chrome playwright mcp --extension","handlingStrategy":"validation","validationCode":"import fs from 'fs';\nasync function canUseExtension(channel: string, executablePath?: string, userDataDir?: string) {\n  if (executablePath) return true; // bypasses profile discovery\n  if (!userDataDir) userDataDir = defaultUserDataDirForChannel(channel);\n  const profile = await findPlaywrightExtensionProfile(userDataDir);\n  return Boolean(profile);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await createExtensionBrowser(channel, executablePath, userDataDir, clientName);\n} catch (e) {\n  if (/Playwright Extension not found/.test((e as Error).message)) {\n    // guide the user to install the extension or fall back to a direct executable\n    throw new Error('Install the Playwright extension or set PLAYWRIGHT_MCP_EXECUTABLE_PATH.');\n  }\n  throw e;\n}","preventionTips":["Run the extension install step as part of environment setup, documented in onboarding.","Set PLAYWRIGHT_MCP_EXECUTABLE_PATH in CI to bypass extension discovery entirely.","Confirm the channel and profile match where the extension was installed."],"tags":["mcp","config","extension","browser","cdp","setup"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}