Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions packages/playwright-core/src/tools/mcp/browserFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ async function createIsolatedBrowser(config: FullConfig, clientInfo: ClientInfo)
handleSIGINT: false,
handleSIGTERM: false,
}).catch(error => {
if (error.message.includes('Executable doesn\'t exist'))
throwBrowserIsNotInstalledError(config);
throwIfExecutableMissing(error, config);
throw error;
});
return browser;
Expand Down Expand Up @@ -171,8 +170,7 @@ async function createPersistentBrowser(config: FullConfig, clientInfo: ClientInf
const browser = browserContext.browser()!;
return browser;
} catch (error: any) {
if (error.message.includes('Executable doesn\'t exist'))
throwBrowserIsNotInstalledError(config);
throwIfExecutableMissing(error, config);
if (error.message.includes('cannot open shared object file: No such file or directory')) {
const browserName = launchOptions.channel ?? config.browser.browserName;
throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`);
Expand Down Expand Up @@ -236,10 +234,15 @@ export function isProfileLocked(userDataDir: string): boolean {
}
}

function throwBrowserIsNotInstalledError(config: FullConfig): never {
const channel = config.browser.launchOptions?.channel ?? config.browser.browserName;
function throwIfExecutableMissing(error: Error, config: FullConfig): void {
// The "Executable doesn't exist" prefix is shared by all managed binaries
// (browser, ffmpeg, winldd). Disambiguate by the path so the user is told
// which dependency to install.
if (!error.message.includes(`Executable doesn't exist`))
return;
const target = error.message.includes('ffmpeg') ? 'ffmpeg' : (config.browser.launchOptions?.channel ?? config.browser.browserName);
const label = target === 'ffmpeg' ? 'FFmpeg' : `Browser "${target}"`;
if (config.skillMode)
throw new Error(`Browser "${channel}" is not installed. Run \`playwright-cli install-browser ${channel}\` to install`);
else
throw new Error(`Browser "${channel}" is not installed. Run \`npx @playwright/mcp install-browser ${channel}\` to install`);
throw new Error(`${label} is not installed. Run \`playwright-cli install-browser ${target}\` to install`);
throw new Error(`${label} is not installed. Run \`npx @playwright/mcp install-browser ${target}\` to install`);
}
35 changes: 35 additions & 0 deletions tests/mcp/video.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

import fs from 'fs';

import { test, expect } from './fixtures';
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';

Expand Down Expand Up @@ -60,6 +62,39 @@ async function closeBrowser(client: Client) {
});
}

test('reports missing ffmpeg, not missing browser, when recordVideo is enabled', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/40862' },
}, async ({ startClient, server, mcpBrowser }, testInfo) => {
test.skip(mcpBrowser !== 'chrome' && mcpBrowser !== 'msedge', 'Channel browsers use system-installed binaries; bundled browsers would also be missing under an empty PLAYWRIGHT_BROWSERS_PATH');

const emptyBrowsersPath = testInfo.outputPath('empty-browsers');
await fs.promises.mkdir(emptyBrowsersPath, { recursive: true });

const { client } = await startClient({
env: { PLAYWRIGHT_BROWSERS_PATH: emptyBrowsersPath },
config: {
browser: {
contextOptions: {
recordVideo: { dir: testInfo.outputPath('videos') },
},
},
},
});

const response = await client.callTool({
name: 'browser_navigate',
arguments: { url: server.HELLO_WORLD },
});
expect.soft(response).toHaveResponse({
isError: true,
error: expect.stringContaining('FFmpeg is not installed'),
});
expect.soft(response).toHaveResponse({
isError: true,
error: expect.not.stringContaining(`Browser "${mcpBrowser}" is not installed`),
});
});

async function produceFrames(client: Client) {
expect(await client.callTool({
name: 'browser_evaluate',
Expand Down
Loading