---
title: "Collect script errors from visitors' · Agent Wranglers"
url: https://agentwranglers.se/playbook/website/when-things-break/browser-error-reports
updated: 2026-10-20
licence: "All rights reserved"
---

# Collect script errors from visitors' browsers

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [When things break](https://agentwranglers.se/playbook/website/when-things-break)
- Rule: Send every uncaught script error and rejected promise from the browser to your own endpoint, and alert when they rise.
- How: Listen for `error` and `unhandledrejection` on `window`. Send the message, the file, the line, the page path and the build with `navigator.sendBeacon`. Send no query string, form values or user ID. Cap reports at a few per page view, and put the endpoint behind the [rate limit](https://agentwranglers.se/playbook/website/security/rate-limits). Group reports by message and build, and alert on new ones after a deploy, as in [alert on symptoms](https://agentwranglers.se/playbook/operations/alerts-and-on-call/alert-on-symptoms).
- Check: On a test page, throw an error and reject a promise. Both appear in the server log within a minute, with the build and path and no query string.

Example:

```js
const MAX_REPORTS = 5;
let sent = 0;
function report(message, source = '', line = 0) {
  if (sent++ >= MAX_REPORTS) return;
  navigator.sendBeacon('/errors', JSON.stringify({ message: String(message).slice(0, 500), source, line, path: location.pathname, build: document.documentElement.dataset.build }));
}
addEventListener('error', e => report(e.message, e.filename, e.lineno));
addEventListener('unhandledrejection', e => report(e.reason?.message ?? e.reason));
```

```python
# Flask: log one line per report, no IP address or cookies
@app.post("/errors")
def browser_errors():
    data = request.get_json(force=True, silent=True) or {}
    app.logger.warning("browser_error build=%s path=%s line=%s message=%r",
        str(data.get("build"))[:40], str(data.get("path"))[:200], data.get("line"), str(data.get("message"))[:500])
    return "", 204
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers website playbook: https://agentwranglers.se/playbook/website/when-things-break/browser-error-reports`.

- Previous: [Serve the last good page when the server fails](https://agentwranglers.se/playbook/website/when-things-break/serve-stale-on-error)
- [When things break](https://agentwranglers.se/playbook/website/when-things-break)
