it49-error-tracker
v1.2.2
Published
Lightweight error capture and classification SDK for websites (React/Next.js/vanilla). Classifies errors in plain language and reports them to a compatible backend.
Maintainers
Readme
it49-error-tracker
Lightweight SDK (no production dependencies) to capture, classify into human-readable categories, and report errors from any website (Next.js, React, or vanilla JS) to a backend compatible with the error-tracking endpoint of api-rentelf-com.
What does it solve?
- Automatic capture of
window.onerror,unhandledrejection, and React render errors (viaErrorBoundary, something awindow.onerrorlistener never catches). - Classifies every error into a category (
code_bug,build_error,api_error,network_error,third_party,user_input,unknown) and a severity (critical,warning,info), and generates a plain-language explanation so anyone on the team understands what happened without reading a stack trace. - Filters out known noise (browser extensions, ResizeObserver, etc.) before reporting.
- Persistent queue (in-memory, with an optional
localStoragemirror) with retries: if the user reloads or loses connection, the error isn't lost. - Automatic breadcrumbs (clicks, navigation) to have context on what the user was doing before the error.
- Suspect identification (
resolveSuspect): on the server, best-effortgit blameon the line that threw, so the report can say who most recently touched that code. - Zero runtime dependencies in the browser — uses native
fetch/sendBeacon. The optional server-sidegit blamefeature uses Node builtins only (child_process/fs), hidden from bundlers so it never breaks the client build.
Installation
npm install it49-error-tracker
# or
yarn add it49-error-trackerUsage in Next.js (pages/_app.js)
import { initErrorTracker, ErrorBoundary } from 'it49-error-tracker';
// Runs once when this module loads (client and server), not inside a
// useEffect: this way it's ready for captureException() anywhere in the
// app (_error.js, getServerSideProps, etc.) without needing to init again.
initErrorTracker({
apiUrl: `${process.env.NEXT_PUBLIC_RENTELF_API}/error-tracking`,
apiKey: process.env.NEXT_PUBLIC_ERROR_TRACKER_KEY, // recommended, see "Security"
clientCode: process.env.NEXT_PUBLIC_CLIENT_CODE, // fallback if there's no apiKey
environment: process.env.NODE_ENV,
release: process.env.NEXT_PUBLIC_RELEASE, // optional: deploy commit/version
});
export default function App({ Component, pageProps }) {
return (
<ErrorBoundary>
<Component {...pageProps} />
</ErrorBoundary>
);
}Usage in SSR / API routes (Node, no window)
import { captureException } from 'it49-error-tracker';
try {
// ...
} catch (error) {
captureException(error, { url: req.url });
}In SSR,
initErrorTracker(...)must also be called (on the server) before usingcaptureException, or the report is discarded.
Manual reporting / breadcrumbs
import { captureException, addBreadcrumb } from 'it49-error-tracker';
addBreadcrumb('User opened the payment modal');
try {
await pay();
} catch (error) {
captureException(error);
}Suspect identification (resolveSuspect)
When enabled, every report generated on the server (SSR: _error.js, getServerSideProps, API routes, custom server, etc.) tries to answer "who most likely introduced this bug?" by running git blame on the line the stack trace points to.
initErrorTracker({
// ...
resolveSuspect: true,
// repoRoot: process.cwd(), // default: the folder the process runs from
});Requires:
- The
gitCLI installed on the server (already true for any server you deploy to withgit pull). - A real git checkout (a
.gitfolder) atrepoRoot, i.e. the deployed folder itself — not a copy without git history.
When it can resolve it, the payload includes a Suspect object:
{
"File": "pages/for-rent-apartment/[pid].js",
"Line": 353,
"Author": "Carlos123",
"Email": "[email protected]",
"Commit": "c710aca79b05",
"CommittedAt": "2025-01-09T19:43:32.000Z",
"Summary": "Update file [pid].js",
"LineContent": "<div>{componentAlerts}</div>",
"Approximate": true
}Important limitations, so it's used correctly:
- Client-side-only errors are never attributed. This never runs in the browser — only errors that go through
captureException/getInitialProps/SSR on the server get aSuspect. A bug that only ever throws after hydration (e.g. inside anonClick) won't have one. - The line number is approximate (
Approximate: true), taken directly from the raw (bundled) stack trace, not verified against source maps. It's most reliable on a production build (next build+next start), where the gap between the original line and the bundled line is small. Innext devthe gap can be larger (Fast Refresh wraps modules), so treat the line as "close" rather than exact — the file and the "most recent author" are always correct even when the exact line drifts a little. - It's opt-in and off by default: it spawns a
gitprocess per error and surfaces author/email, which your team should explicitly decide to enable.
Security: apiKey vs clientCode
The backend supports two modes:
- Recommended —
apiKey: request an API key per site from the backend team (POST /keys/:clientCodeinapi-rentelf-com, requires an admin session). TheClientCodeis derived from the key in a verified way (Verified: true) and cannot be spoofed from the browser. - Legacy —
clientCode: if there's noapiKey, the client code is sent directly (Verified: false). Still works for backwards compatibility, but anyone could send reports with someone else'sClientCode.
initErrorTracker options
| Option | Type | Description |
|---|---|---|
| apiUrl | string | URL of the error-tracking endpoint |
| apiKey | string? | Site API key (recommended) |
| clientCode | string? | Client code (legacy mode) |
| environment | string? | production / staging / development. Outside of production, errors are only logged to the console. |
| release | string? | Version/commit of the current deploy |
| userId | string? | Id of the authenticated user (never sensitive data) |
| ignore | (string\|RegExp)[]? | Additional patterns to ignore |
| maxBreadcrumbs | number? | Default 20 |
| dedupeWindowMs | number? | Default 5 minutes |
| enabled | boolean? | Fully disable the tracker |
| beforeSend | (payload) => payload \| null | Inspect/mutate/discard before sending |
| resolveSuspect | boolean? | Server-only: attach a Suspect via git blame. Default false. See Suspect identification |
| repoRoot | string? | Repo root for git blame. Default process.cwd() |
Troubleshooting
If reports aren't showing up in the backend, check the browser/server console: this SDK never fails silently when a send attempt is actually made.
[it49-error-tracker] (not sent, environment != production): you're not inproduction(or didn't passenvironment: 'production'). The error was classified correctly but intentionally not sent — this is expected outside of production.[it49-error-tracker] API rejected the error report (400 ...): the backend rejected the payload. Usually means the API is running an older version that doesn't accept the fields this SDK sends (Category,Severity,HumanMessage,Breadcrumbs, etc.) — redeploy the backend.[it49-error-tracker] Failed to reach the error-tracking API: network issue reachingapiUrl(wrong URL, CORS, backend down).[it49-error-tracker] Giving up on an error report after 3 failed attempts: the report was retried and discarded — check the warning right above it for the root cause.
Development
yarn install
yarn build # generates dist/ (ESM + CJS + .d.ts)
yarn typecheck