mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-06 14:00:09 +02:00
* fix(realtime): drop WS frames without a string type (MUL-3418)
The onmessage handler dispatched every parsed frame to onAny and the
ws.on subscribers without validating its shape. A frame whose parsed JSON
lacked a string `type` (an out-of-protocol frame injected by a proxy /
extension, or a bare JSON primitive) reached the realtime-sync onAny
dispatcher, where `msg.type.split(":")` threw an uncaught TypeError out of
onmessage. The browser reported it via window.onerror, flooding PostHog
`$exception` (~12k events). Non-fatal (no crash, connection stays up), but
pure noise.
Validate once at this trust boundary: a frame must be an object carrying a
string `type`, otherwise drop it as a no-op. The bad-frame log is
rate-limited to one entry per connection — a misbehaving source can repeat
the frame hundreds of times per session.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* feat(analytics): drop benign ResizeObserver exceptions from telemetry
ResizeObserver "loop ..." errors are the dominant `$exception` bucket
(~31k events / ~1.5k users). They are a benign, self-recovering browser
quirk with no actionable signal and otherwise drown real failures and burn
the event budget. Drop them entirely in before_send (ahead of redaction
and the dedupe fuse, which only caps repeats). The match is narrow — only
the benign "loop" phrasing — so a genuine ResizeObserver bug still reports.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
53 lines
2.3 KiB
TypeScript
53 lines
2.3 KiB
TypeScript
// Known-benign browser exceptions that are pure noise in `$exception`
|
|
// telemetry. These are dropped ENTIRELY in `before_send` (not merely deduped by
|
|
// exception-dedupe.ts) — they carry no actionable signal, the browser
|
|
// self-recovers, and at scale they dominate the error stream, drowning real
|
|
// failures and burning the billed event budget.
|
|
//
|
|
// ResizeObserver "loop ..." errors are the canonical case: the spec fires them
|
|
// when observation callbacks don't settle within a single animation frame. The
|
|
// browser resumes delivery on the next frame, so nothing actually breaks. Every
|
|
// app that uses ResizeObserver (directly or via a UI library) emits them. The
|
|
// CSSWG explicitly considers them benign — see w3c/csswg-drafts#5023. Across
|
|
// Chrome versions the message is either "ResizeObserver loop limit exceeded"
|
|
// (older) or "ResizeObserver loop completed with undelivered notifications"
|
|
// (newer); both contain "ResizeObserver loop".
|
|
//
|
|
// The bar for adding a pattern here is high: it must be a benign,
|
|
// self-recovering error with no actionable signal. A real bug must never be
|
|
// silenced — when unsure, leave it to the dedupe fuse, which only caps repeats.
|
|
|
|
const BENIGN_MESSAGE_PATTERNS: RegExp[] = [/ResizeObserver loop/i];
|
|
|
|
/**
|
|
* Whether this `$exception` event is known-benign browser noise that should be
|
|
* dropped entirely. Reads the message from the (pre-redaction) event
|
|
* properties — the matched messages carry no PII, so reading them raw is safe,
|
|
* and matching before redaction avoids any chance of a scrub mangling the
|
|
* signal. Never throws: any unexpected shape returns `false` (keep the event),
|
|
* the fail-open direction `before_send` requires.
|
|
*/
|
|
export function isBenignException(
|
|
properties: Record<string, unknown> | undefined,
|
|
): boolean {
|
|
if (!properties || typeof properties !== "object") return false;
|
|
|
|
const messages: unknown[] = [properties.$exception_message];
|
|
const list = properties.$exception_list;
|
|
if (Array.isArray(list)) {
|
|
for (const entry of list) {
|
|
if (entry && typeof entry === "object" && "value" in entry) {
|
|
messages.push((entry as { value: unknown }).value);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const message of messages) {
|
|
if (typeof message !== "string") continue;
|
|
for (const pattern of BENIGN_MESSAGE_PATTERNS) {
|
|
if (pattern.test(message)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|