diff --git a/.claudedocs/sentry-triage/2026-02-19.md b/.claudedocs/sentry-triage/2026-02-19.md deleted file mode 100644 index c079e8be..00000000 --- a/.claudedocs/sentry-triage/2026-02-19.md +++ /dev/null @@ -1,32 +0,0 @@ -# Sentry Triage — 2026-02-19 - -Commit: `09174fd` on `main` - -## Issues Triaged (5) - -### ACTIONABLE — Fixed in Code - -| ID | Title | Events | Users | Fix | -|---|---|---|---|---| -| WORLDMONITOR-1G | `Error: ML request unload-model timed out after 120000ms` | 30 | 27 | Wrapped `unloadModel()` in try/catch; timeout no longer leaks as unhandled rejection. Cleans up `loadedModels` set on failure. | -| WORLDMONITOR-1F | `Error: ML request unload-model timed out after 120000ms` | 9 | 9 | Same root cause as 1G (different release build hash). | -| WORLDMONITOR-1K | `TypeError: this.player.playVideo is not a function` | 1 | 1 | Added optional chaining (`playVideo?.()`, `pauseVideo?.()`) in `LiveNewsPanel.ts`. YT IFrame API player object may not have methods ready during initialization race. | - -### NOISE — Filtered - -| ID | Title | Events | Users | Filter | -|---|---|---|---|---| -| WORLDMONITOR-1J | `InternalError: too much recursion` | 1 | 1 | i18next internal `translate -> extractFromKey` cycle on Firefox 147. Added `/too much recursion/` to `ignoreErrors`. | -| WORLDMONITOR-1H | `TypeError: Cannot read properties of null (reading 'id')` | 1 | 1 | maplibre-gl internal render crash (`_drawLayers -> renderLayers`). Extended `beforeSend` regex to suppress null `id`/`type` when stack is in map chunk. | - -## Files Modified - -| File | Change | -|---|---| -| `src/services/ml-worker.ts` | `unloadModel()`: try/catch around `this.request()`, clean `loadedModels` on failure | -| `src/components/LiveNewsPanel.ts` | Optional chaining on `playVideo?.()` and `pauseVideo?.()` | -| `src/main.ts` | Added `/too much recursion/` to `ignoreErrors`; extended maplibre `beforeSend` filter for null `id`/`type` | - -## Sentry Status - -All 5 issues marked **resolved (in next release)** via API. They will auto-reopen if errors recur after deployment. diff --git a/.gitignore b/.gitignore index 077bfa24..e0473ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ src-tauri/sidecar/node/* # Compiled sebuf gateway bundle (built by scripts/build-sidecar-sebuf.mjs) api/[[][[].*.js +.claudedocs/ diff --git a/src/main.ts b/src/main.ts index cd59fd5d..8d955d10 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,7 @@ Sentry.init({ ignoreErrors: [ 'Invalid WebGL2RenderingContext', 'WebGL context lost', - /reading 'imageManager'/, + /imageManager/, /ResizeObserver loop/, /NotAllowedError/, /InvalidAccessError/, @@ -94,6 +94,10 @@ Sentry.init({ /Cannot define multiple custom elements/, /maxTextureDimension2D/, /Container app not found/, + /this\.St\.unref/, + /Invalid or unexpected token/, + /evaluating 'elemFound\.value'/, + /Cannot access '\w+' before initialization/, ], beforeSend(event) { const msg = event.exception?.values?.[0]?.value ?? ''; @@ -101,13 +105,17 @@ Sentry.init({ const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? []; // Suppress maplibre internal null-access crashes (light, placement) only when stack is in map chunk if (/this\.style\._layers|reading '_layers'|this\.light is null|can't access property "(id|type|setFilter)", \w+ is (null|undefined)|Cannot read properties of null \(reading '(id|type|setFilter|_layers)'\)|null is not an object \(evaluating '(E\.|this\.style)|^\w{1,2} is null$/.test(msg)) { - if (frames.some(f => /\/(map|deck-stack)-[A-Za-z0-9]+\.js/.test(f.filename ?? ''))) return null; + if (frames.some(f => /\/(map|maplibre|deck-stack)-[A-Za-z0-9-]+\.js/.test(f.filename ?? ''))) return null; } // Suppress any TypeError that happens entirely within maplibre or deck.gl internals if (/^TypeError:/.test(msg) && frames.length > 0) { - const appFrames = frames.filter(f => f.in_app && !/\/sentry-[A-Za-z0-9]+\.js/.test(f.filename ?? '')); - if (appFrames.length > 0 && appFrames.every(f => /\/(map|deck-stack)-[A-Za-z0-9]+\.js/.test(f.filename ?? ''))) return null; + const appFrames = frames.filter(f => f.in_app && !/\/sentry-[A-Za-z0-9-]+\.js/.test(f.filename ?? '')); + if (appFrames.length > 0 && appFrames.every(f => /\/(map|maplibre|deck-stack)-[A-Za-z0-9-]+\.js/.test(f.filename ?? ''))) return null; } + // Suppress YouTube IFrame widget API internal errors + if (frames.some(f => /www-widgetapi\.js/.test(f.filename ?? ''))) return null; + // Suppress Sentry SDK internal crashes (logs.js) + if (frames.some(f => /\/ingest\/static\/logs\.js/.test(f.filename ?? ''))) return null; return event; }, });