Content process OOMs to ~15 GB on ad-heavy page with uBlock Origin excluded
Describe the bug:
With the bundled uBlock Origin excluded, loading an ad-heavy page causes the Firefox content
process to grow to ~15 GB and crash. Playwright then reports Target crashed on the next call.
The parent process is unaffected, so this is not an IPC or protocol problem.
Measured live during a crash (ps -eo rss):
content process: 15,393 MB <- the tab
parent process: 479 MB <- flat throughout, before and afterThe browser's own stderr (via DEBUG=pw:browser) shows the allocator finally giving up:
[pid=45][err] out of memory: 0x00000000000000A0 bytes requested
!!! PAGE CRASH EVENTA 160-byte allocation failing on a host with 51 GB free is the tail end of the runaway, not the cause.
What I verified:
- Happens on v152.0.4-beta.28 and v152.0.4-beta.30 (latest) — roughly 4 of every 5 runs.
- Does not happen on the previous stack:
camoufox==0.4.11/playwright==1.50.0/ browserv135.0.1-beta.24— 3/3 clean on the identical script and page, with uBlock Origin excluded exactly the same way. That is the last configuration I know to be good. - Does not happen when uBlock Origin is left enabled — 3/3 clean, page loads instantly.
- Not environment-related: reproduces outside Docker, on the host, with a default
camoufox fetchinstall, no proxy, and the snippet below as the entire script. Also ruled out cgroup limits (none), host memory (51 GB free) and/dev/shm(still crashes at--shm-size=2g). - No interaction required. Load the page and make no further calls at all — the tab still
crashes. It is not triggered by anything the automation does; any later call merely surfaces it.
The delay before
Target crashedis the process climbing to 15 GB, not a hang. beta.28andbeta.30share the sameSourceStamp(e39c605adc0fc049a165d7fe4a3f6517b761edf7), which is presumably why the newer build doesn't help.
The page pulls a fairly typical adult-site ad stack — acscdn.com/script/aclib.js,
creative.xlviiirdr.com/... (compiles asm.js), camhours.com widgets, hotjar. Nothing exotic.
Caveats, in the interest of not overstating:
- A stock Firefox 154 on the same machine renders the page without trouble. I did not audit that profile's extensions, so I can't rule out that it has its own ad blocking — treat that comparison as suggestive rather than controlled.
- The site rotates ad creatives continuously, so I cannot guarantee the old and new stacks were served byte-identical payloads. The 3/3 vs 4/5 split is consistent across a couple of hours, but treat it as strong rather than airtight.
- The test URL is a NSFW adult site. It's the case I have that reproduces reliably; I'd expect any sufficiently ad-saturated page to do the same, but I haven't found a SFW equivalent.
Screenshots:
N/A — the failure is a frozen then dead tab; the memory figures above are the useful artifact.
To Reproduce:
import time
from camoufox.sync_api import Camoufox
from camoufox import DefaultAddons
# exclude_addons is the only non-default argument. With uBlock Origin enabled this never reproduces.
with Camoufox(exclude_addons=[DefaultAddons.UBO]) as browser:
page = browser.new_context().new_page()
page.on("crash", lambda _: print("!!! CRASH"))
page.goto("https://camstreams.tv/", timeout=60000) # NSFW
time.sleep(150) # make no further calls - the tab dies on its ownThat is the whole reproduction: open the page and wait. No interaction with the page is needed.
(time.sleep blocks sync Playwright's event dispatch, so the crash callback only prints once the
sleep returns — the crash itself happens during the wait.)
Any call made after the crash raises, if you would rather see it as an exception:
page.evaluate("() => 1+1") # Error: Page.evaluate: Target crashedRun with DEBUG=pw:browser to see the out of memory line from the browser itself, and watch
ps -eo rss,args | grep camoufox during the pause to see the content process climb.
Stack trace:
From the real application rather than the snippet (our own frames renamed). The call that surfaces it is incidental. Any call made after the crash raises the same way.
Traceback (most recent call last):
File "/app/worker.py", line 40, in handle_task
scraper.start()
File "/app/scraper.py", line 150, in start
self.perform_action(self.handle_website_entry)
File "/app/browser_base.py", line 464, in handle_website_entry
buttons = self.page.query_selector_all('button, input[type="button"], div[role="button"]')
File "/usr/local/lib/python3.10/dist-packages/playwright/sync_api/_generated.py", line 8857, in query_selector_all
self._sync(self._impl_obj.query_selector_all(selector=selector))
File "/usr/local/lib/python3.10/dist-packages/playwright/_impl/_sync_base.py", line 115, in _sync
return task.result()
File "/usr/local/lib/python3.10/dist-packages/playwright/_impl/_page.py", line 418, in query_selector_all
return await self._main_frame.query_selector_all(selector)
File "/usr/local/lib/python3.10/dist-packages/playwright/_impl/_frame.py", line 380, in query_selector_all
await self._channel.send(
File "/usr/local/lib/python3.10/dist-packages/playwright/_impl/_connection.py", line 69, in send
return await self._connection.wrap_api_call(
File "/usr/local/lib/python3.10/dist-packages/playwright/_impl/_connection.py", line 563, in wrap_api_call
raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
playwright._impl._errors.Error: Page.query_selector_all: Target crashedOn 0.5.4 / Playwright 1.60.0 the same failure surfaced as the vaguer
playwright._impl._errors.TargetClosedError: Target page, context or browser has been closed.
Playwright 1.61.0 reports it correctly as Target crashed.
Version:
Python Packages
Camoufox v0.5.6
Browserforge v1.2.4
Apify Fingerprints v0.15.0
Playwright v1.61.0
Browser
Active official/stable
Current browser v152.0.4-beta.30
Build date Sep 1
SHA256 5720d45b894cHost: Ubuntu 22.04.5 LTS, kernel 6.8.0-60-generic, Python 3.10.12, 62 GB RAM.
Source: daijro/camoufox