fix(ui): log global errors silently instead of popping a toast + overlay
The global error handler (Vue errorHandler + window error + unhandledrejection) fired a red 'Something went wrong: <raw msg>' toast AND an auto on-device overlay on every caught error — deliberately loud for bug-bash, but it surfaces benign, non-actionable noise (e.g. a transient RPC rejection during a ws reconnect, or the service worker failing to register over a self-signed cert) right in the user's face. Demote the catch-all to SILENT capture: keep console.error + the window.__archyErrors ring buffer, and expose the screenshot-able overlay on-demand via window.__archyShowErrors() — but never auto-pop. Components that need to report a specific, actionable failure still call toast.error() directly. Also filter known-benign environmental noise (PWA service-worker registration failing over a self-signed cert — needs a trusted cert, #56) so it doesn't even occupy a ring-buffer slot and push out real errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0406af522c
commit
2d8ade629b
@ -23,8 +23,6 @@ if (!navigator.clipboard) {
|
||||
},
|
||||
})
|
||||
}
|
||||
import { useToast } from '@/composables/useToast'
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
@ -97,14 +95,20 @@ function recordError(source: string, err: unknown, info?: string) {
|
||||
const entry: ArchyErrorEntry = { when: new Date().toISOString(), source, message, info, stack: e?.stack }
|
||||
errorLog.push(entry)
|
||||
if (errorLog.length > 25) errorLog.shift()
|
||||
// Log SILENTLY: a global handler error is almost always something we should
|
||||
// fix at the source, not interrupt the user for. Keep the full record on the
|
||||
// console + the window.__archyErrors ring buffer, and make the screenshot-able
|
||||
// overlay available ON DEMAND (window.__archyShowErrors(), or the debug view)
|
||||
// — but do NOT auto-pop a red toast / overlay over the UI. Components that
|
||||
// need to tell the user about a *specific, actionable* failure still call
|
||||
// toast.error() directly; this catch-all stays out of the way.
|
||||
console.error(`[${source}]`, err, info ?? '')
|
||||
// Surface the real message (truncated) instead of a generic toast — this is a
|
||||
// test/bug-bash build, and "Something went wrong" hides exactly what we need.
|
||||
const short = message.length > 140 ? `${message.slice(0, 140)}…` : message
|
||||
try {
|
||||
useToast().error(`Something went wrong: ${short}`)
|
||||
} catch { /* toast itself failed — the console + ring buffer still have it */ }
|
||||
// Always show the on-device overlay so the error is visible without a console.
|
||||
}
|
||||
|
||||
// Expose the on-demand error overlay + ring buffer so a crash that only repros
|
||||
// in a runtime without a console (Android companion WebView) is still
|
||||
// retrievable: call `window.__archyShowErrors()` to screenshot/Copy them.
|
||||
;(window as unknown as { __archyShowErrors?: () => void }).__archyShowErrors = () => {
|
||||
try { showErrorOverlay() } catch { /* overlay is best-effort */ }
|
||||
}
|
||||
|
||||
@ -133,15 +137,28 @@ function reloadOnceForStaleChunk(err: unknown): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// Known-benign environmental noise — expected on some deployments and not
|
||||
// actionable by the user or us, so it shouldn't even occupy a ring-buffer slot
|
||||
// (which would push out real errors). The PWA service worker can't register
|
||||
// over a self-signed cert (it needs a trusted cert or localhost); on those
|
||||
// nodes the SW/offline cache simply doesn't run, which is fine. Logged at debug
|
||||
// only. (A trusted cert is the real fix — tracked separately, #56.)
|
||||
function isBenignEnvironmentError(err: unknown): boolean {
|
||||
const msg = (err as { message?: string })?.message ?? String(err ?? '')
|
||||
return /Failed to register a ServiceWorker|ServiceWorker.*(SSL|certificate|SecurityError)|An SSL certificate error occurred when fetching the script/i.test(msg)
|
||||
}
|
||||
|
||||
// Vue's errorHandler only catches errors raised synchronously inside Vue's
|
||||
// lifecycle/reactivity. Async rejections and plain runtime errors (e.g. a JS
|
||||
// API missing in an older WebView) slip past it, so catch those too.
|
||||
window.addEventListener('error', (ev) => {
|
||||
if (reloadOnceForStaleChunk(ev.error ?? ev.message)) return
|
||||
if (isBenignEnvironmentError(ev.error ?? ev.message)) { console.debug('[benign]', ev.message); return }
|
||||
recordError('window.error', ev.error ?? ev.message)
|
||||
})
|
||||
window.addEventListener('unhandledrejection', (ev) => {
|
||||
if (reloadOnceForStaleChunk(ev.reason)) return
|
||||
if (isBenignEnvironmentError(ev.reason)) { console.debug('[benign]', ev.reason); return }
|
||||
recordError('unhandledrejection', ev.reason)
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user