Error Tracing
NodeTool records server failures, failed workflow runs and client crashes as redacted error traces in its own database. Agents can query them, a bug report can attach them, and on the hosted service they are queryable in Supabase. Nothing is sent to a third-party error tracker.
What is recorded
| Source | When |
|---|---|
trpc |
A tRPC procedure fails with a 5xx status |
http |
A REST handler throws with a 5xx status |
job |
A workflow run fails, or fails before it starts |
web / electron |
The route error boundary or a panel error boundary catches a crash |
Client mistakes (4xx) are not traced. Each trace stores the error type, the
message, the stack, a fingerprint that groups repeats, the app version, the
platform, and a small context. The context keeps only identifiers listed in
ERROR_TRACE_CONTEXT_KEYS (packages/models/src/error-trace-redaction.ts):
job, workflow, node and thread ids, the route, the HTTP status and similar.
Prompts, node inputs, outputs and request bodies are never stored.
Redaction
redactErrorTrace runs before every insert, on both the local capture path
and the sync ingest path. It removes provider credentials by shape, JWTs,
bearer tokens, PEM keys, secret-named key=value pairs, signed URL
parameters, URL credentials, the values of credential variables in the server
environment, email addresses, non-loopback IPv4 addresses, home-directory
names, data: URLs and long opaque strings. Messages are capped at 2,000
characters and stacks at 40 frames. The table is in the
personal-data registry, so
account export includes the traces and account erasure deletes them.
A process stores at most 10 traces of one fingerprint and 300 traces in total per minute. Traces older than 30 days are pruned at startup and once a day.
Reading traces
| Surface | How |
|---|---|
| Agents (chat and MCP) | list_error_traces, get_error_trace, export_error_report |
| CLI | nodetool errors list, show <id>, export [ids…] [-o file], sync, prune |
| tRPC | errorTraces.list, summary, get, report |
| Bug report dialog | The “Recent server errors” section attaches the last hour as server-errors.md |
Every surface reads only the caller’s own traces. A trace id can be given in full or as its 12-character prefix.
Supabase
On a deployment whose database is Supabase PostgreSQL, the traces live in
nodetool_error_traces in that database. The create_error_traces migration
enables row-level security and adds the error_traces_owner_read policy, so
the Supabase Data API returns a signed-in user only their own rows and returns
nothing to anonymous callers. The server connects as the table owner and is
not affected. Query across users in the Supabase SQL editor, for example:
SELECT fingerprint, count(*), max(created_at) AS last_seen, min(message) AS message
FROM nodetool_error_traces
WHERE created_at > (now() - interval '1 day')::text
GROUP BY fingerprint
ORDER BY count(*) DESC;
Syncing a local install
Sync is off by default. To push a local install’s traces to a NodeTool server, set both variables:
| Variable | Value |
|---|---|
NODETOOL_ERROR_TRACE_SYNC_URL |
The server’s base URL. Must be https, except for localhost |
NODETOOL_ERROR_TRACE_SYNC_TOKEN |
An access token for your account on that server |
The server then pushes unsynced local traces every 5 minutes to
errorTraces.ingest, and nodetool errors sync pushes them on demand. The
receiving server stores them under the token’s user and redacts them again.
Received traces are never forwarded. A local install never holds database
credentials for the cloud.
Other settings
| Variable | Effect |
|---|---|
NODETOOL_ERROR_TRACES |
0, false or off stops server-side capture |
NODETOOL_ERROR_TRACE_RETENTION_DAYS |
Retention window in days. Default 30 |