Platform
Errors to tasks
How errors from the apps, GitHub Actions, Coolify and store syncs become tasks on Leitagu Tasks by themselves, without flooding the board: fingerprints, counts, regressions, mute, limits, scrubbing and setup.
Updated 23 October 2026View as Markdown
Bugs and failures become tasks on Leitagu Tasks by themselves, so the team and its AI agents can claim and fix them. Each distinct error is one task. Repeats raise a counter on it instead of opening new tasks. No paid service is involved: everything runs in the Leitagu apps.
Where errors come from
| Source label | What reports it | When |
|---|---|---|
app-error |
Every app (shop, admin, partner, CRM, docs, tasks) through apps/<app>/src/instrumentation.ts |
An unhandled server error in a page, route handler or server action (Next.js onRequestError), an API route that answered 500, an uncaught exception or an unhandled promise rejection. 4xx answers, "not found" and redirects are not errors. |
deploy |
.github/workflows/images.yml |
A build, smoke test or push of an app's image fails on main, or deploying it through Coolify fails. One task per job and app. Pull request builds are not reported. |
coolify |
Coolify's webhook notifications | A deployment fails, a database or storage backup fails or is missing, a scheduled task fails, an application stops or keeps restarting, a container restarts by itself, a server becomes unreachable, disk usage is high, and a few more. |
sync |
POST /api/cron/sync in the partner app |
A store connection's scheduled sync fails (SyncFailed) or saves only part of the products (SyncPartial, low priority). One task per connection and kind. |
What a task looks like
- Title:
[app] ErrorType: short message, for example[web] TypeError: Cannot read properties of undefined. - Labels:
bug,auto, the source label above and the app label (web,partner,server-<name>…). - Project:
errors. Unassigned, so anyone can claim it. Created by the agent "Error reporter". - Priority: high for crashes, failed deploys and backups, stopped apps, unreachable servers, and server errors on sign-in, sign-up, password and checkout pages; low for partial syncs and warnings; medium for the rest.
- Description: first and last time, count, environment, the commit it ran (
LEITAGU_VERSION), the request method and path (never the query string or body), the message, the stack with our own code's frames first, command output for Coolify jobs, and links (the GitHub Actions run, the Coolify deployment, the store connection).
Not flooding the board
- Fingerprint. Source + app + error type + the message with ids, numbers, hashes and long quoted values replaced + the top frame of our own code + the route. Deploy, sync and Coolify reports give a stable key instead (for example
images/image/webor the Coolify database's uuid). The same error after a new deploy keeps its fingerprint because build hashes and line numbers are left out. - One task per fingerprint.
task_error_groups(migration026_task_error_groups) keeps the fingerprint, its task, the count, first and last time, and when the task last got a comment. - Repeats raise the count and the last time. The task gets a comment with the latest sample at most once an hour (once a day for low-priority errors).
- Regressions. If the task is done and the error comes back, the task opens again as "to do" with the label
regressionand a comment. - Mute. The label
muted(the Mute button on the task and in the Errors view) stops comments and reopening; the count still goes up. Use it for an error that needs no work, such as a store whose products often lack sizes. - Limits. At most 10 new error tasks an hour from one source and 20 in all. Past that, new errors are still counted but wait without a task, and a single error storm task (label
error-storm, high priority) lists them. A waiting error gets its own task when it comes back after the hour. - In each app the reporter sends the first occurrence of an error at once and counts repeats for a minute, then sends them as one report with the count. A process sends at most 30 reports a minute, so a crash loop can't flood Tasks. Reporting never waits on the request (3-second timeout) and never throws.
The Errors view
Errors in the Tasks menu (/errors) lists one row per error: its task, status, priority, count, last and first time, source and app, with a Mute button. It shows open errors by default; "All" includes done ones, and the source chips filter. Errors waiting because of the hourly limit show "Waiting (hourly limit)". The task page shows the same count and times in an Error report box.
What is never stored
Reports are scrubbed in the app and again in Tasks before anything is saved: query strings and fragments, request bodies, cookies, Authorization headers, Bearer and Basic credentials, tokens and API keys (Leitagu, Anthropic, Resend, GitHub, Shopify, WooCommerce, Stripe and others, JWTs, private keys), passwords and other secret=…-style values, passwords in database addresses, email addresses, long digit runs (phone, card and personal code numbers), and long random-looking strings. Only http(s) links are kept, without their query strings.
Ingest endpoints
All under https://tasks.leitagu.ee/api/v1/ingest/, POST with JSON, answered with 202 and what happened (created, counted, commented, reopened, muted or limited) and the task key.
| Path | Body | Token |
|---|---|---|
/error |
One report: source (app-error, deploy, coolify, sync), app, type, message, and optionally stack, output, severity (critical, error, warning), environment, version, request (method, path, route), links (url, title), key, count, details |
Authorization: Bearer lgt_tasks_… |
/coolify |
Coolify's webhook payload as it sends it | The header, or ?token=lgt_tasks_… |
/coolify/<token> |
The same | In the address, because Coolify's webhook can only be given an address |
Only ingest tokens are accepted: tokens with the scope tasks:ingest and nothing else. An ingest token can't list, read or change tasks (403 on every other endpoint and on MCP), and read/write tokens and staff sessions can't send reports. Each token may send 120 reports a minute; bodies over 64 KB are refused. The Tasks app records its own errors straight into its database instead of calling itself.
Setting it up
- Ingest token. In Leitagu Tasks → Agents, create a token for the agent Error reporter with only Report errors only (tasks:ingest) ticked. Copy it: it is shown once. One token can serve every app, GitHub and Coolify; separate tokens (same agent name) make it possible to revoke one place alone.
- Apps. On each app (shop, admin, partner, CRM, docs) set
TASKS_INGEST_URL=https://tasks.leitagu.ee/api/v1/ingest/errorandTASKS_INGEST_TOKEN=<the token>, then redeploy. Without both, the reporter does nothing. The Tasks app needs neither: it reports to itself in production (TASKS_SELF_REPORT=offturns that off). - GitHub Actions. Add the repository secret
LEITAGU_TASKS_INGEST_TOKENwith the token. Without it the report steps print a line and do nothing. - Coolify. In Coolify → Notifications → Webhook, set the webhook URL to
https://tasks.leitagu.ee/api/v1/ingest/coolify/<the token>, enable the channel and send the test notification (Tasks answers it and ignores it). Turn on the events Deployment failure, Resource status changes, Restart limit reached, Backup failure, Scheduled task failure, Docker cleanup failure, Disk usage warning and Server unreachable. Success events are ignored, so leaving them on only adds traffic. - Store syncs need nothing more: the partner app's
TASKS_INGEST_*settings cover them.
Coolify 4.4 has no separate "unhealthy" event: an unhealthy container shows up as a restart (container_restarted, under Resource status changes) or as a stopped application.
Code
packages/core/src/shared/errorReport.ts: the report format, scrubbing, fingerprints, title, labels, priority and description.packages/core/src/server/errorReporter.tsanderrorReporterNode.ts: the reporter in every app.packages/core/src/server/errorIngest.ts: groups, counts, comments, regressions, mute, limits and the storm task; the Errors view's queries.packages/core/src/server/errorIngestApi.tsandpackages/core/src/shared/coolify.ts: the endpoints and the Coolify mapping.packages/core/src/server/integrations/syncReport.ts: sync failures and partial syncs.- Tests:
packages/core/tests/errors.test.ts,e2e/tasks.spec.ts.
Planned
- A small, sampled reporter for browser errors in the admin, CRM and partner apps.