HarperFast / HarperFast/studio
[RUM] One failed route query emits up to 14 identical error-boundary reports, and a 403 renders a raw "Component Error" dead end (84 sessions/30d)
- Dominant language
- TypeScript
- Stars
- 5
- Forks
- 4
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 40
Description
Every `@error.source:custom` event in Studio's RUM comes from `main.tsx`'s React root error
handlers calling `addReactError` (`@datadog/browser-rum-react`). In the last 30 days there are
**170 such events across 84 sessions** — 3.3% of the 2,556 sessions in that window. Every one is a
*data-fetch failure that reached a route error boundary*, so those users got `ErrorComponent`
instead of the page they asked for.
Two separable defects sit behind that number.
## 1. One failed query emits N identical Error Tracking events
Worst session in the window: a single `GET /Organization/{id}` returning **403**. React Query made
3 HTTP attempts (all 403). RUM recorded **14 error events in 378 ms**, and all 14 are:
- `error.source: custom`, `handling: handled`
- `handling_stack` → `at onCaughtError @ /assets/index-D9wn3O9N.js:825:43564` (identical offset)
- a **byte-identical 46-line `component_stack`**
So 13 of the 14 carry no information the first one didn't, and each ships ~4 KB of component stack.
Because Datadog fingerprints by stack, they also all collapse into one Error Tracking issue whose
event count is ~5× the number of failures that actually happened — which makes any "is this getting
worse?" comparison on this family meaningless.
The 30-day average is 2.0 events per affected session, so the tail is what hurts: 14, 9, 6, 5, 5, 5, 5.
## 2. An expected-but-unauthorized fetch crashes the route
`src/router/useNewRouter.ts:25` sets `defaultErrorComponent: ErrorComponent`, and
`src/components/ErrorComponent.tsx` renders `{error.message}` verbatim inside a red card titled
**"Component Error"**. For the 403 above the user's screen reads:
> **Component Error**
> Request failed with status code 403
> [ ← Return to Home ]
A 403 means the user is authenticated but lacks permission on that org/cluster/instance — an
expected state that deserves an access-denied view, not a component-crash card showing raw Axios
text. The same path swallows recoverable failures: over 30 days the messages behind these events are
403 (majority), 500, 404, and `Network Error`, and a `Network Error` or 500 should offer a retry
rather than a dead end.
## Distinct from #1546
#1546 is about authenticated requests firing *while signed out* on `/` and `/sign-in`. Here the user
is signed in and keeps working in the same session after the crash (they went on to edit a cluster).
Some of the 30-day sample does land on `/sign-in?redirect=…` hashes and is #1546; the org/cluster/
instance route crashes are not.
## Verification
- Deployed prod build `index-D9wn3O9N.js` (`v2.169.3`, `Last-Modified: Fri, 28 Aug 2026 15:30:23 GMT`)
contains `onCaughtError:(e,t)=>{We(e,t),console.error("Caught error:",e,t)}` — confirmed by fetching
the chunk and reading line 825 at the offset the RUM `handling_stack` points to.
- `error.issue.first_seen_version` on this family is **v2.94.4**, so it is long-standing, not a
regression. It is simply unreported.
- All 14 events in the burst session were confirmed individually to originate at `onCaughtError`.
## Side note: the redundant `console.error`
All three handlers in `main.tsx` call `addReactError(...)` **and then** `console.error(...)` with the
same error. RUM instruments `console.error`, so that second call is a second report of an error
already reported — the anti-pattern #1672 was filed for. In practice only 1 console-sourced
counterpart showed up for the 14 `custom` events above, so the console leg mostly does not reach
Error Tracking today and I did not establish why. It is still worth removing, for two reasons:
- On the SDK the repo now pins (`7.10.0`, prod runs `7.9.0`), `buildConsoleLog` does
`rawError.message = params.map(format).join(' ')`, so when that call *does* land the RUM message
becomes `"Caught error: {componentStack…}"` — a multi-kilobyte multi-line string. Long
multi-line messages are silently omitted from `@error.message` group-bys, so those events become
invisible to the usual triage query.
- `console.debug` is not collected by RUM, so it keeps the devtools breadcrumb without the duplicate.
Contributor guide
Research direction
Start by reading main.tsx to trace the three React root error handlers and their addReactError and console.error calls. Then inspect src/router/useNewRouter.ts:25 and src/components/ErrorComponent.tsx to understand the route fallback. Done means failed queries no longer create redundant reports and 403, 500, 404, and network failures receive appropriate recovery views instead of the raw Component Error card.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100