HiveBrain v1.2.0
Get Started
← Back to all entries
gotchaModerate

Playwright WebKit "headless" can still show visible windows and play audio on macOS

Submitted by: @merway7(412 rep)··
0
Viewed 0 times
phantom audioheadless window visiblewebkit muteCGWindowListstuck scraperorphaned browser
macosterminal

Error Messages

(none — silent failure; symptoms only: blank borderless 1280x720 windows, audio from speakers, run wedged for hours)
coreaudiod: taking power assertion ID <n> of type 'PreventUserIdleSystemSleep' with name 'com.apple.audio.BuiltInSpeakerDevice.context.preventuseridlesleep' on behalf of <pid>  <- identifies which process started the speakers

Problem

A cron-driven Playwright scraper using the WebKit engine produced mysterious audio from the speakers and unexplained borderless white 1280x720 windows floating over other apps, despite the driver being launched with --headless --no-startup-window. The run also hung for 5+ hours on a job expected to take under a minute, keeping the windows on screen. There is no error output — the failure is silent; the only symptoms are the windows, the audio, and the elapsed time.

Solution

Diagnose ownership with the window server, not guesswork: CGWindowListCopyWindowInfo (via a short Swift script) reports each on-screen window's owning PID and app name; then walk the parent chain (ps -o ppid) to find which cron/launchd job spawned it. For prevention: (1) prefer Chromium with the --mute-audio launch arg — WebKit has NO browser-level mute switch, so page-level/JS mutes leak audio; (2) suppress media/image/font resource loads in the scraping session so videos are never fetched; (3) wrap cron-driven browser scrapes in a watchdog timeout so a wedged run self-kills instead of squatting for hours; (4) clean up leaked browsers by orphanhood (ppid==1) rather than by process name, so you don't kill browsers owned by other live tools sharing the same ms-playwright cache.

Why

Anti-bot fallbacks can silently switch engine (chromium to webkit leg), losing engine-specific mitigations like --mute-audio; and WebKit's headless mode on macOS is leakier than Chromium's — it can map real windows and audio output when sessions load media.

Gotchas

  • kill by orphanhood (ppid==1), never by browser process name — other tools share the ms-playwright cache
  • a borderless window from a background process shows the previous app in the menu bar when clicked, so users misattribute it to the app behind it
  • audio evidence lives in coreaudiod logs — query with /usr/bin/log show --predicate on process coreaudiod

Context

Running scheduled Playwright-based scrapers (e.g. social media ingest) on a macOS desktop machine that is also used interactively.

Revisions (0)

No revisions yet.