Skip to content

Count Chromium net::ERR_* failures per VM - #320

Open
ulziibay-kernel wants to merge 1 commit into
mainfrom
feat/track-chrome-net-errors
Open

Count Chromium net::ERR_* failures per VM#320
ulziibay-kernel wants to merge 1 commit into
mainfrom
feat/track-chrome-net-errors

Conversation

@ulziibay-kernel

@ulziibay-kernel ulziibay-kernel commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Moved here from kernel/kernel-images-private#319 per @rgarcia's review — this has no private-only dependencies and the public repo owns both the metrics and DevTools proxy infrastructure, so landing upstream avoids fork-only behavior and the sync conflict that PR would have created.

Why

We have no way to answer "how often are our sessions hitting ERR_HTTP2_PROTOCOL_ERROR?" — the error that keeps showing up in the Akamai/RST_STREAM investigation. The signal exists in three places today, none of which work for fleet-wide tracking:

Source Why it doesn't work
Chrome UMA Net.ErrorCodesForMainFrame4 Recorded in the renderer process (record_load_histograms.cc), so Browser.getHistograms can't see it on our images
cdpmonitor network_loading_failed events Only runs when a caller opts into a CDP telemetry category, and the events go to their sinks, not ours
/curl transport errors Only covers the /curl endpoint, not browser traffic

What

Tap the DevTools proxy — the one component that is on for every session — and tally Network.loadingFailed by error text and resource type.

  • server/lib/neterrorTracker accumulates counts for the life of the VM. errorText must carry the net::ERR_ prefix, so an unexpected protocol string can't consume a label slot. Distinct keys are capped at 256 with an overflow counter.
  • server/lib/devtoolsproxy — the existing MessageTransform hook now also offers upstream→client frames to the tracker. The tap is passive: it never rewrites or injects CDP traffic, so it stays invisible to automation in the browser.
  • server/lib/wsproxy — the "->"/"<-" direction strings the pump already passed to MessageTransform are now exported constants, so the tap's upstream-only check reads as intent rather than a magic string.
  • server/lib/metrics — new collector exposing:
kernel_chromium_net_errors_total{error="net::ERR_HTTP2_PROTOCOL_ERROR",resource_type="Document"} 12
kernel_chromium_net_errors_dropped_total 0

Unlike the Chrome collector this touches neither Chrome nor the network on scrape; it just reads the tally. There's also a throttled (1/min per error class) WARN log line so a single VM can be triaged from /var/log/supervisord/kernel-images-api without waiting for a scrape.

Review feedback from #319

  • Belongs upstream — done, this PR.
  • HELP/TYPE without a sample isn't a queryable series — correct, the comment claiming otherwise was wrong and is gone. A VM with no failures is now genuinely absent from the labelled family; kernel_chromium_net_errors_dropped_total is always sampled and is what distinguishes "no failures" from "not scraped". TestNetErrorCollectorWithNoFailures asserts this.
  • Should errorText require a net::ERR_ prefix? — yes, good catch. Added, with TestObserveRequiresNetErrorPrefix.
  • Proxy-level test for the direction check — added TestWebSocketProxyHandler_ObservesOnlyUpstreamFrames with a recording observer. I verified it's not vacuous by flipping the constant and watching it fail.
  • Bugbot: route.abort() isn't fully excluded — accurate, and the old wording oversold the filtering. Playwright's default route.abort() reason surfaces as net::ERR_FAILED with canceled false, which is indistinguishable from a genuine generic failure. I chose to keep counting it rather than drop a real error class: the per-error label means a session blocking images inflates only the ERR_FAILED series and never the specific ones this exists to track. Documented in the package doc and pinned by TestObserveCountsErrFailedSeparately.

Cost

The relay path is hot, so every frame is prescreened with a substring scan and only decoded if the needle is present:

BenchmarkObserveNonFailure-14   10641642   108.1 ns/op   0 B/op   0 allocs/op

Caveats

  • Coverage depends on the client enabling the Network domain. Playwright, Puppeteer, and the CUA app all do by default, but a raw CDP client that never sends Network.enable contributes nothing. This was the trade for being always-on rather than gated behind telemetry opt-in.
  • No URL/host label. Network.loadingFailed doesn't carry the URL — correlating it means tracking requestWillBeSent state on the relay path, and host is too high-cardinality for a Prometheus label anyway. If the counter shows real volume, host attribution is a good follow-up as a log field.
  • Single source by design. Only the proxy is instrumented, not cdpmonitor. They hold separate CDP connections and would each see the same failure, so counting both would double.

Test plan

  • go vet ./... clean, gofmt clean
  • go test -race passes for lib/neterror, lib/metrics, lib/devtoolsproxy
  • Direction test verified non-vacuous by mutation (flip the constant → fails)
  • Full unit suite passes (two lib/recorder TestFFmpegArgs_* failures reproduce on clean main — pre-existing, unrelated)
  • Deploy to staging, force an HTTP/2 failure, confirm the counter increments on :10002/metrics
  • Wire into a dashboard/alert once we see baseline volume

Note

Medium Risk
Changes the hot WebSocket relay path with per-frame substring scans (mitigated by prescreening) and bounded metric cardinality; behavior is read-only for CDP clients but depends on Network domain being enabled for coverage.

Overview
Adds fleet-visible Prometheus counters for Chromium net::ERR_* failures by passively tapping relayed CDP traffic on the always-on DevTools WebSocket proxy.

A process-wide neterror.Tracker parses upstream→client frames for Network.loadingFailed, counts by errorText and resource type (skipping cancellations/ERR_ABORTED, requiring the net::ERR_ prefix), caps distinct label pairs at 256 with an overflow counter, and emits throttled WARN logs. The DevTools proxy wires this through a new optional NetErrorObserver on the existing message transform (client frames are not scanned). /metrics gains kernel_chromium_net_errors_total and always-on kernel_chromium_net_errors_dropped_total via NetErrorCollector. wsproxy exports direction constants so the upstream-only tap is explicit.

Counts are cumulative per VM and only reflect failures when the CDP client has Network enabled; scrape reads in-memory tallies without talking to Chrome.

Reviewed by Cursor Bugbot for commit 693321f. Bugbot is set up for automated code reviews on this repo. Configure here.

We can't currently tell how often sessions hit network failures like
ERR_HTTP2_PROTOCOL_ERROR. Chrome tracks this in the UMA histogram
Net.ErrorCodesForMainFrame4, but that is recorded in the renderer process
and so is invisible to Browser.getHistograms on these images, and the
cdpmonitor events that do carry errorText only flow when a caller opts
into network telemetry.

Tap the CDP proxy instead, the one vantage point that is on for every
session, and tally Network.loadingFailed by error text and resource type.
Cancellations and ERR_ABORTED are dropped so real failures aren't buried;
route.abort() with its default reason is reported as ERR_FAILED with
canceled false, which is indistinguishable from a genuine failure and is
counted in its own series rather than dropped. The counts surface on
/metrics as kernel_chromium_net_errors_total, with a throttled log line
per error class for per-VM triage.

The tap is passive and prescreens each frame with a substring scan before
decoding, so non-failure frames cost ~108ns and zero allocations. The
"->"/"<-" direction strings the pump already passed to MessageTransform
are now named constants, so the tap's upstream-only check reads as intent
rather than as a magic string.

Co-authored-by: Cursor <cursoragent@cursor.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