Skip to content

Fix two Chromium browser-process crashes (history embeddings CHECK, macOS --single-process) - #54

Open
goeric wants to merge 3 commits into
simonw:mainfrom
goeric:fix/chromium-browser-process-crashes
Open

Fix two Chromium browser-process crashes (history embeddings CHECK, macOS --single-process)#54
goeric wants to merge 3 commits into
simonw:mainfrom
goeric:fix/chromium-browser-process-crashes

Conversation

@goeric

@goeric goeric commented Jul 27, 2026

Copy link
Copy Markdown

Summary

The Chromium process rodney manages dies mid-session, after which every command
fails with failed to connect to browser (is it still running?). It turned out
to be two independent Chromium CHECK failures in the browser process, both
caused by launch flags rodney chooses. 33 crash dumps on one macOS machine over
five days; both are fixed here.

Neither is a segfault — EXC_BREAKPOINT/SIGTRAP in the browser process is a
deliberate Chromium abort, so the browser takes itself down and there is nothing
to recover.

Root cause 1 — the field-trial testing config runs unfinished features

FATAL:history_embeddings_service.cc(597)] Check failed:
cached_embedding != embedding_cache.end()

The pinned Chromium (128.0.6568.0, rod revision 1321438) is a development
snapshot
, and those builds apply
testing/variations/fieldtrial_testing_config.json by default. That
force-enables features that are still being written. HistoryEmbeddings is one:
it computes passage embeddings on the browser's main thread after each
navigation and CHECK-fails on a cache miss, so the whole browser aborts a few
seconds after a text-heavy page loads.

It needs the on-device model the optimization guide downloads, so it only starts
biting once a profile has been used for a while. That is why a long-lived
~/.rodney profile crashes reliably while a throwaway one looks fine — and why
this presents as "heavy commercial sites kill the browser". The command that
reports the failure is just the next one after the browser already died.

Fix: --disable-field-trial-config, so rodney drives a browser with shipped
defaults rather than whatever was mid-flight when the snapshot was cut, plus an
explicit --disable-features=HistoryEmbeddings because a browser supplied via
ROD_CHROME_BIN can enable it from a server-side variations seed that the switch
does not cover.

Root cause 2 — --single-process on macOS

FATAL:video_capture_device_factory_apple.mm(37)] Check failed: mode.
The MacOS video capture code must be run on a CFRunLoop-enabled thread

--single-process (added for gVisor screenshots, notes/gvisor-screenshots/)
collapses the utility services into the browser process. media/capture's Apple
backend CHECKs that it owns a CFRunLoop-enabled thread, which only holds when
video capture has a utility process of its own.

Deterministic, one command on macOS:

rodney open https://example.com/
rodney js "navigator.mediaDevices.enumerateDevices().then(d=>d.length)"
# before: browser gone. after: 3

Device enumeration is routine in the fingerprinting scripts commercial sites
ship, which is what makes it look load-related. gVisor is a Linux sandbox, so
skipping the flag on Darwin costs nothing; Linux and Windows are unchanged.

This overlaps #45 by @flgusto, which reached the same flag from crash
reports. It is included so the branch is a complete fix — drop commit 2 if #45
lands first.

The two are independent

Fixing only --single-process still crashes: with the flag already absent, a
profile that reproduces root cause 1 died 3/3 runs. Both fixes are needed.

Verification

Against a copy of a profile that reproduces the crash, driving real commercial
sites:

before after
history_embeddings CHECK (real profile) died 3/3 survived 4/4
enumerateDevices() probe (macOS) died 2/2 survived 3/3
end-to-end browse loop, 75 commands new crash dump 2/2 none 2/2
soak: 240 commands / 48 page loads alive, 0 new crash dumps

Crash sites were confirmed two ways that agree exactly: symbolizing the .ips
dumps by disassembling the stripped framework at the faulting return address to
recover the embedded __FILE__/condition strings, and then capturing the same
FATAL: lines live on stderr from a reproduced crash.

go build, go vet and the full go test ./... suite pass. Five unit tests
cover the flag selection, including that --disable-features accumulates rather
than clobbers what rod and configureExtensions already set.

(Independent of #52 — these flags are on main and the earliest dumps predate
that branch.)

🤖 Generated with Claude Code

goeric and others added 3 commits July 27, 2026 11:15
rodney's pinned Chromium (128.0.6568.0) is a development snapshot, and
those builds apply testing/variations/fieldtrial_testing_config.json by
default. That config force-enables features which are still being
written, and one of them takes the browser process down:

    FATAL:history_embeddings_service.cc(597)] Check failed:
    cached_embedding != embedding_cache.end()

HistoryEmbeddings computes passage embeddings on the browser's main
thread after a navigation, so the CHECK aborts the whole browser rather
than one tab. Every later rodney command then fails with "failed to
connect to browser (is it still running?)".

It only fires once the optimization guide has downloaded its on-device
model, which is why a long-lived ~/.rodney profile crashes within
seconds on a text-heavy page while a throwaway profile looks fine -- and
why this reads as "heavy commercial sites kill the browser".

Verified against a copy of a profile that reproduces it: 3/3 runs
crashed before this change, 0/4 after.

--disable-field-trial-config drops the testing config wholesale so
rodney drives a browser with shipped defaults. HistoryEmbeddings is
named explicitly as well, since a browser supplied through
ROD_CHROME_BIN can enable it from a server-side variations seed that the
switch does not cover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
--single-process collapses Chromium's renderer, GPU and utility services
into the browser process. It was added so screenshots work under gVisor,
whose multi-process compositor IPC hangs (notes/gvisor-screenshots/), but
on macOS the collapse is fatal: media/capture's Apple backend CHECKs that
it owns a CFRunLoop-enabled thread, which only holds when video capture
has a utility process of its own.

    FATAL:video_capture_device_factory_apple.mm(37)] Check failed: mode.
    The MacOS video capture code must be run on a CFRunLoop-enabled thread

Any page that touches navigator.mediaDevices therefore kills the whole
browser. Device enumeration is routine in the fingerprinting scripts
commercial sites ship, so this reads as "complex sites crash Chromium".

Reproduces in one command on macOS, and stops reproducing with this
change:

    rodney open https://example.com/
    rodney js "navigator.mediaDevices.enumerateDevices().then(d=>d.length)"

gVisor is a Linux sandbox, so skipping the flag on Darwin costs nothing;
Linux and Windows behaviour is unchanged.

This overlaps PR simonw#45 by @flgusto, which diagnosed the same flag from
crash reports. Kept here so the branch is a complete fix; drop this
commit if simonw#45 lands first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The doc argues for --single-process and is the reason the flag exists, so
record why macOS no longer gets it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant