v135 · network
Error reporter
Errors thrown late in a session — during page unload, after a crash prompt, or at navigation — are often lost with fetch() because the page may be discarded before the request completes. fetchLater() queues the report durably: it fires when the page is hidden or after a timeout, surviving navigations.
Feature detection: checking…
fetch() — unreliable on unload
fetch('/report', { method: 'POST', body: … }) inside beforeunload or pagehide is not guaranteed to complete. Browsers may discard in-flight requests when navigating away. Keepalive fetch helps but has payload limits and can't be retried.
fetchLater() — deferred and durable
fetchLater('/report', { method: 'POST', body: … }) registers a deferred request that the browser guarantees to send when the page enters the back/forward cache or is discarded. Works across navigations. No retry logic needed.
Error simulator — trigger errors to queue reports
ready
Report queue
queued: 0
flushed: 0
would-have-lost: 0
No reports yet — trigger an error above.
Delivery guarantee comparison
fetchLater() delivered
0
fetch() would deliver
0
fetch() on unload lost
0
// Error reporter using fetchLater()
class ErrorReporter {
constructor(endpoint) {
this._endpoint = endpoint;
this._buffer = [];
this._deferredHandle = null;
}
report(level, message, context = {}) {
const payload = {
level, message, context,
url: location.href,
ts: Date.now(),
userAgent: navigator.userAgent,
};
this._buffer.push(payload);
// Register/update the deferred request
this._deferredHandle?.abort();
this._deferredHandle = fetchLater(this._endpoint, {
method: 'POST',
body: JSON.stringify(this._buffer),
headers: { 'Content-Type': 'application/json' },
// activateAfter: fires after 30s even if page stays open
activateAfter: 30_000,
});
}
}
// Auto-capture unhandled errors
window.addEventListener('error', e => reporter.report('error', e.message));
window.addEventListener('unhandledrejection', e =>
reporter.report('warn', String(e.reason)));