Frontend error tracking: what to log and what to ignore
Buganalyst
Turn on any frontend error tracker for the first time and the experience is always the same: a firehose. Thousands of events overnight. Errors from browser extensions, from ad blockers mangling your scripts, from bots executing half your JavaScript, from networks that dropped mid-request. Somewhere in there are the four errors that actually matter to your users, and they are indistinguishable from the noise — which is how teams end up with an error channel that everyone has muted.
Frontend error tracking is valuable, but only after deliberate curation. The goal is not to capture everything; it is to maintain a signal your team actually reads.
Why the browser is such a noisy place
Unlike your server, the browser is territory you do not control. Your code runs alongside a dozen extensions the user installed, through networks of arbitrary quality, on browser versions spanning a decade, visited by crawlers that execute JavaScript badly. A large fraction of the errors thrown on your pages were not thrown by your code and cannot be fixed by you. Accepting this early shapes everything else: your job is separating your defects from the environment's weather.
Errors worth waking up for
A few categories deserve loud, immediate visibility. Errors thrown from your own bundle in a core user flow — checkout, signup, save — top the list; each one is a user hitting a wall. Unhandled promise rejections in your API layer usually mean a failure path you never wrote handling for. Rendering crashes (a React error boundary firing, a blank component tree) mean users are staring at nothing. And any spike that begins minutes after a deploy is your regression announcing itself; deploy-correlated novelty is the single most useful alarm in frontend monitoring.
Noise worth filtering aggressively
Equally important is the discard pile. Errors originating from extension code paths and injected scripts are not yours. Failures from known crawler user agents are not users. Network errors on flaky connections tell you nothing actionable in the individual case (though a sustained rise in aggregate can flag a real availability problem). The classic benign entries — the ResizeObserver loop warning, script-error events from cross-origin scripts with no detail attached — belong in the filter list of every project. Filtering these is not sweeping problems under the rug; it is refusing to let the environment spam your team.
A practical rule: any error class that has fired a thousand times without producing one user complaint or one reproducible defect is a candidate for the mute list. Review the list quarterly rather than never.
The context that makes an error debuggable
An error message without context is trivia. The same message with context is a work item. The high-value fields are the URL and route, the release version (so you can diff against the previous one), the browser and viewport, and — most underrated — a breadcrumb trail of the last several user actions before the crash. "TypeError: x is undefined" is unfixable in isolation; the same error, on the settings page, immediately after "clicked save with an empty display-name field," on the release deployed this morning, is a fifteen-minute fix.
Group errors by root cause rather than raw message where possible: one bug typically produces dozens of message variants across browsers, and ungrouped duplicates bury everything else.
Automated tracking still misses the worst bugs
Here is the uncomfortable gap in every error-tracking setup: the most damaging frontend failures often throw no exception at all. The form that validates forever and never submits. The button that does nothing. The total that displays the wrong number. The layout that renders unusable at tablet width. Your tracker is silent on all of these, because from JavaScript's perspective, nothing went wrong.
This is why automated capture and human reporting are complements, not substitutes. An embedded bug report widget catches precisely the category your error tracker cannot: things that are wrong without being exceptions. And because a widget report arrives with URL, viewport, and user agent attached, it slots into the same triage pipeline as your tracked errors — one stream of machine-detected failures, one stream of human-detected ones, both with reproduction context built in.
A setup that stays useful
The sustainable configuration is boring and effective: track errors with release tagging and breadcrumbs, filter the known-noise classes ruthlessly, alert only on core-flow errors and post-deploy spikes, and give humans a low-friction way to report what the machines cannot see. Review the top ten error groups weekly in ten minutes. A quiet channel that people trust beats a comprehensive one that people mute — in monitoring, attention is the scarcest resource you manage.