Bug Description
When using chromium.connectOverCDP() from a Linux client to connect to a Windows CDP server, file downloads fail because Playwright sends Linux paths (/tmp/playwright-artifacts-XXXXXX) to Windows Chrome, which cannot write to these paths.
Environment
- Playwright Version: 1.57.0 (latest)
- Client OS: Linux (Ubuntu 22.04, Alpine, etc.)
- Server OS: Windows 10/11
- Browser: Chromium-based (Chrome, Edge)
- Connection Method:
chromium.connectOverCDP()
Reproduction
Minimal Test Case
const playwright = require('playwright-core');
async function reproduce() {
// Connect to Windows CDP server
const browser = await playwright.chromium.connectOverCDP('ws://windows-host:9222/devtools/browser/...');
// Create context with acceptDownloads
const context = await browser.newContext({ acceptDownloads: true });
const page = await context.newPage();
// Trigger download
const downloadPromise = page.waitForEvent('download');
await page.goto('https://example.com/file.pdf');
const download = await downloadPromise;
// This will fail with ENOENT
await download.saveAs('./local-file.pdf');
await browser.close();
}
reproduce();
Expected Behavior
File should be downloaded and saved to ./local-file.pdf on the Linux client.
Actual Behavior
Error: download.saveAs: ENOENT: no such file or directory,
copyfile '/tmp/playwright-artifacts-MedLPD/537add71-ff6a-4dfa-85e7-f6f2f68080f8'
Root Cause Analysis
1. Path Generation on Linux Client
In packages/playwright-core/src/server/chromium/chromium.ts:
// Line 122-124
const artifactsDir = await progress.race(fs.promises.mkdtemp(ARTIFACTS_FOLDER));
const browserOptions: BrowserOptions = {
// ...
downloadsPath: options.downloadsPath || artifactsDir, // ← Linux path!
// ...
};
The artifactsDir is created on the Linux client using fs.promises.mkdtemp().
2. CDP Command Sent to Windows
In packages/playwright-core/src/server/chromium/crBrowser.ts:
// Line 351-358
promises.push(this._browser._session.send('Browser.setDownloadBehavior', {
behavior: this._options.acceptDownloads === 'accept' ? 'allowAndName' : 'deny',
browserContextId: this._browserContextId,
downloadPath: this._browser.options.downloadsPath, // ← Linux path sent to Windows!
eventsEnabled: true,
}));
This Linux path (/tmp/playwright-artifacts-XXXXXX) is sent to Windows Chrome via CDP.
3. Windows Chrome Cannot Write
Windows Chrome receives the Linux path and attempts to write to it, resulting in:
- Path doesn't exist
- Permission errors
- ENOENT error
4. saveAs() Fails
In packages/playwright-core/src/client/artifact.ts:
// Line 45-55
async saveAs(path: string): Promise<void> {
if (!this._connection.isRemote()) {
await this._channel.saveAs({ path });
return;
}
// Remote connection - tries to copy from local path
const result = await this._channel.saveAsStream();
// ... but the file is on Windows, not Linux!
}
The saveAs() method tries to copy from a local Linux path that doesn't exist.
Impact
Affected Users
- Cross-platform testing teams (Linux CI → Windows browser)
- Docker users (Linux container → Windows host browser)
- Remote browser services (Browserbase, Browserless, etc.)
- Enterprise testing (Linux servers → Windows test machines)
Workarounds
- Network Interception (Complex, manual implementation)
- File Server (Requires additional server on Windows)
- Direct HTTP Download (Not always possible)
- Switch to Puppeteer (Better CDP support for large files)
Proposed Solutions
Solution 1: Path Translation (Recommended)
Add automatic path translation for cross-platform CDP connections:
// In chromium.ts connectOverCDPInternal
if (options.isLocal === false) {
// Detect target OS and use appropriate path
const targetOS = detectTargetOS(wsEndpoint);
if (targetOS === 'windows') {
downloadsPath: 'C:\\Users\\Administrator\\Downloads\\playwright-downloads';
} else if (targetOS === 'linux') {
downloadsPath: '/tmp/playwright-downloads';
}
}
Solution 2: Document the Limitation
Add clear warning in official documentation:
Warning: When using connectOverCDP() to connect to a remote browser on a different operating system, file downloads may fail due to path translation issues. Consider using:
- Network interception with
IO.read
- File server approach
- Direct HTTP downloads
Solution 3: Provide Official Workaround
Create a helper function or middleware:
// New API
const browser = await chromium.connectOverCDP({
endpointURL: 'ws://windows-host:9222/devtools/browser/...',
downloadsPath: 'C:\\Users\\Administrator\\Downloads\\playwright-downloads' // Windows path
});
Related Issues
Additional Context
Playwright's Official Stance
From issue #34542 (closed as "not planned"):
"This is expected behavior. When connecting over CDP, Playwright cannot access the remote browser's filesystem."
Counter-argument: Playwright CAN access remote files via:
- Network interception +
IO.read (CDP command)
- File server approach
- Direct HTTP downloads
The limitation is implementation choice, not technical impossibility.
Comparison with Puppeteer
Puppeteer handles large file transfers (>50MB) over CDP without issues, while Playwright has a 50MB hard limit.
Suggested Labels
bug
cross-platform
CDP
download
connectOverCDP
help-wanted
Suggested Priority
Medium - Affects a significant subset of users doing cross-platform testing, but has workarounds.
Additional Files
/root/Documents/playwright_cdp_download.md - Complete research on CDP download behavior
/root/Documents/playwright_cdp_download研究报告.md - Technical analysis in Chinese
/root/Documents/bug_analysis.md - Detailed bug analysis
Conclusion
This is a design flaw in Playwright's cross-platform CDP implementation. The issue is well-documented in multiple GitHub issues, but remains unresolved. A fix would significantly improve the cross-platform testing experience for many users.
Bug Description
When using
chromium.connectOverCDP()from a Linux client to connect to a Windows CDP server, file downloads fail because Playwright sends Linux paths (/tmp/playwright-artifacts-XXXXXX) to Windows Chrome, which cannot write to these paths.Environment
chromium.connectOverCDP()Reproduction
Minimal Test Case
Expected Behavior
File should be downloaded and saved to
./local-file.pdfon the Linux client.Actual Behavior
Root Cause Analysis
1. Path Generation on Linux Client
In
packages/playwright-core/src/server/chromium/chromium.ts:The
artifactsDiris created on the Linux client usingfs.promises.mkdtemp().2. CDP Command Sent to Windows
In
packages/playwright-core/src/server/chromium/crBrowser.ts:This Linux path (
/tmp/playwright-artifacts-XXXXXX) is sent to Windows Chrome via CDP.3. Windows Chrome Cannot Write
Windows Chrome receives the Linux path and attempts to write to it, resulting in:
4. saveAs() Fails
In
packages/playwright-core/src/client/artifact.ts:The
saveAs()method tries to copy from a local Linux path that doesn't exist.Impact
Affected Users
Workarounds
Proposed Solutions
Solution 1: Path Translation (Recommended)
Add automatic path translation for cross-platform CDP connections:
Solution 2: Document the Limitation
Add clear warning in official documentation:
Solution 3: Provide Official Workaround
Create a helper function or middleware:
Related Issues
video.saveAsfails on windows #30016: video.saveAs fails on windows (similar issue)Additional Context
Playwright's Official Stance
From issue #34542 (closed as "not planned"):
Counter-argument: Playwright CAN access remote files via:
IO.read(CDP command)The limitation is implementation choice, not technical impossibility.
Comparison with Puppeteer
Puppeteer handles large file transfers (>50MB) over CDP without issues, while Playwright has a 50MB hard limit.
Suggested Labels
bugcross-platformCDPdownloadconnectOverCDPhelp-wantedSuggested Priority
Medium - Affects a significant subset of users doing cross-platform testing, but has workarounds.
Additional Files
/root/Documents/playwright_cdp_download.md- Complete research on CDP download behavior/root/Documents/playwright_cdp_download研究报告.md- Technical analysis in Chinese/root/Documents/bug_analysis.md- Detailed bug analysisConclusion
This is a design flaw in Playwright's cross-platform CDP implementation. The issue is well-documented in multiple GitHub issues, but remains unresolved. A fix would significantly improve the cross-platform testing experience for many users.