← Back to microsoft/playwright
microsoft / playwright · Issue No. 41356
1.58.2
This issue is difficult to reproduce because it occurs rarely. When we run few instances of Firefox using Playwright, launch may fail with the following error:
playwright-scanner | File "/app/playwright_scanner.py", line 58, in scan_url
playwright-scanner | browser = await p.firefox.launch(
playwright-scanner | ^^^^^^^^^^^^^^^^^^^^^^^
playwright-scanner | File "/app/.venv/lib/python3.12/site-packages/playwright/async_api/_generated.py", line 14547, in launch
playwright-scanner | await self._impl_obj.launch(
playwright-scanner | File "/app/.venv/lib/python3.12/site-packages/playwright/_impl/_browser_type.py", line 97, in launch
playwright-scanner | await self._channel.send(
playwright-scanner | File "/app/.venv/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 69, in send
playwright-scanner | return await self._connection.wrap_api_call(
playwright-scanner | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
playwright-scanner | File "/app/.venv/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 559, in wrap_api_call
playwright-scanner | raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
playwright-scanner | playwright._impl._errors.Error: BrowserType.launch: ENOENT: no such file or directory, stat '/ms-playwright/firefox-1509/firefox/lock'
When two independent Firefox processes are active, it looks like they may race on nsUpdateMutex crossProcessLock (https://searchfox.org/firefox-main/rev/1f6f9eea1aa79f25fee9e961a6bebed59b41d415/toolkit/xre/nsUpdateMutex.cpp#33). Lock is automatically deleted on browser close and another instance may try to check already deleted lock, leading to an error.
The lock is created, because it looks like automatic updates of Firefox are not actually disabled by https://github.com/microsoft/playwright/blob/3d38b1d3a38ba378f522554b063be070747b5a26/browser_patches/firefox/preferences/playwright.cfg#L170-L180.
When we try to run Firefox browser with app.update.log.file enabled:
>>> from playwright.sync_api import sync_playwright
>>> p = sync_playwright().start()
>>> browser = p.firefox.launch(firefox_user_prefs={"app.update.log": True, "app.update.log.file": True})
After 15-30 seconds, we may see that UpdateService is actually active and checking for updates:
root@31dae1651755:/app# stat /ms-playwright/firefox-1509/firefox/lock
File: /ms-playwright/firefox-1509/firefox/lock -> 172.18.0.5:+16912
Size: 17 Blocks: 0 IO Block: 4096 symbolic link
Device: 0,237 Inode: 38545047 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2026-06-18 11:53:05.328595934 +0000
Modify: 2026-06-18 11:53:05.288507078 +0000
Change: 2026-06-18 11:53:05.288507078 +0000
Birth: 2026-06-18 11:53:05.288507078 +0000
root@31dae1651755:/app# head /tmp/playwright_firefoxdev_profile-WyWz5S/update_messages.log
AUS:SVC Creating UpdateService
AUS:SVC Logging current UpdateService status:
AUS:SVC UpdateService.canUsuallyCheckForUpdates - able to check for updates
AUS:SVC UpdateService.canCheckForUpdates - able to check for updates
AUS:SVC getCanApplyUpdates - testing write access /ms-playwright/firefox-1509/firefox/update.test
AUS:SVC isAppBaseDirWritable - testing write access for /ms-playwright/firefox-1509/firefox/update.test
AUS:SVC isAppBaseDirWritable - Path is writable: /ms-playwright/firefox-1509/firefox/update.test
AUS:SVC getCanApplyUpdates - able to apply updates
AUS:SVC gCanStageUpdatesSession - testing write access /ms-playwright/firefox-1509/firefox/update.test
AUS:SVC gCanStageUpdatesSession - testing write access /ms-playwright/firefox-1509/update.test
After diving into Firefox code, we may see that update service can be effectively disabled only by setting policies
/**
* See nsIUpdateService.idl
*/
get updateDisabled() {
return (
(Services.policies && !Services.policies.isAllowed("appUpdate")) ||
this.updateDisabledForTesting ||
Services.sysinfo.getProperty("isPackagedApp")
);
}
When I try to set the DisableAppUpdate policy to true, by providing my own policies.json file:
root@31dae1651755:/app# printf '{"policies": {"DisableAppUpdate": true}}' > policies.json
root@31dae1651755:/app# python
Python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from playwright.sync_api import sync_playwright
>>> p = sync_playwright().start()
>>> browser = p.firefox.launch(env={"PLAYWRIGHT_FIREFOX_POLICIES_JSON": "/app/policies.json"})
I don't see the update lock and update log file doesn't appear in the profile.
No response
Docker image: mcr.microsoft.com/playwright:v1.58.2-noble
System:
OS: Linux 7.0 Ubuntu 24.04.3 LTS 24.04.3 LTS (Noble Numbat)
Container: Yes
Binaries:
Node: 24.13.0 - /usr/bin/node
Yarn: 1.22.22 - /usr/bin/yarn
npm: 11.6.2 - /usr/bin/npm
Languages:
Bash: 5.2.21 - /usr/bin/bash
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.