Send unhandled Python exceptions straight to a Telegram chat. No backend, no account, no database — just your own bot token.
Project description
devalerts
Send unhandled Python exceptions straight to a Telegram chat — the moment they happen, on your phone. No backend, no account, no database — just your own bot token.
import devalerts
devalerts.init(bot_token="123456:ABC-DEF...", chat_id=123456789)
That's the whole setup. Two minutes with @BotFather and every unhandled crash — including ones raised in threads — lands in your chat instead of a log file nobody's watching.
Why devalerts
- Zero infrastructure. No SaaS signup, no ingestion server, no API key to manage beyond your own Telegram bot token. State lives in a local SQLite file you already own.
- One line to install, one line to wire up.
init()installs the hook and gets out of the way. - Not spam. Errors are grouped by fingerprint and rate-limited per group, so a crash loop sends one message, not a thousand.
- Framework-aware. Ships an ASGI middleware for FastAPI/Starlette apps, where the default excepthook would never even see a request error.
- Small and typed. No dependencies, ships
py.typed, ~450 lines total — short enough to read in one sitting before you trust it with your errors.
Install
uv add devalerts
(or pip install devalerts if you're not using uv)
Usage
- Create a bot with @BotFather and get its token.
- Message your bot once (or add it to a group) so it's allowed to message you back.
- Get your chat id — message @userinfobot, or call
https://api.telegram.org/bot<TOKEN>/getUpdatesafter step 2 and readmessage.chat.id. - In your app, as early as possible:
import devalerts
devalerts.init(bot_token="123456:ABC-DEF...", chat_id=123456789)
That's it — any unhandled exception (including ones raised in threads) now also lands in your Telegram chat.
Grouping, rate limiting, and the dashboard
Exceptions are grouped by fingerprint (exception type + file + line where it
was raised) in a local SQLite file (~/.devalerts/state.db). Each group
sends at most one Telegram message per rate_limit_seconds (default 300);
repeats inside that window are counted but not sent, and the next message
for that group says how many were skipped. Old groups (untouched for 7 days)
are pruned automatically. Configure the window via init():
devalerts.init(bot_token="...", chat_id=123456789, rate_limit_seconds=60)
See what's grouped and what's currently rate-limited:
uv run devalerts dashboard
ID TYPE LOCATION LAST SEEN TOTAL STATUS
────────────────────────────────────────────────────────────────────────────────────
a1b2c3d4 ValueError app/orders.py:42 2m ago 14 ● limited
9f8e7d6c KeyError app/handlers/webhook.py:88 just now 1 ● sending
2 error groups, 1 currently rate-limited.
Manually reporting a caught exception
try:
risky_call()
except Exception:
devalerts.report() # sends the currently-handled exception
or:
with devalerts.capture():
risky_call() # reports on exception, then re-raises
capture also works as a decorator, so you don't need to touch a function's
body at all:
@devalerts.capture()
def risky_call():
...
FastAPI / Starlette / any ASGI app
init()'s excepthook won't see request errors — the framework already catches
them internally to return a 500 response, so nothing "unhandled" ever reaches
the process. Use the ASGI middleware instead:
app.add_middleware(devalerts.ASGIMiddleware)
Only exceptions that actually escape as server errors get reported — routing
404s and raised HTTPExceptions are already turned into responses by the
framework before the middleware sees them.
devalerts vs. a full error tracker
If you already run Sentry/Rollbar/etc., keep using it — this isn't a replacement. devalerts is for the side project, internal tool, or small service that doesn't have (and doesn't want) that infrastructure yet:
| devalerts | Sentry-style tracker | |
|---|---|---|
| Setup | one bot token | account + project + SDK config |
| Backend | none — Telegram only | hosted or self-hosted service |
| Where alerts land | your Telegram chat | a web dashboard |
| Grouping / rate limiting | yes, local SQLite | yes, server-side |
| Search, trends, releases | no | yes |
What this does NOT do (by design)
- Grouping/rate limiting is local and in-process only (SQLite file, no server) — the dashboard is a CLI table, not a web UI.
- No backend, no accounts — each user runs their own bot.
- Basic secret redaction only (a few common token patterns) — do not rely on this for sensitive production data.
- No automated test suite — verified manually during implementation only.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file devalerts-0.1.4.tar.gz.
File metadata
- Download URL: devalerts-0.1.4.tar.gz
- Upload date:
- Size: 9.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cfff35e9a13c9672248da414ee7c72b5488d25bcca49dd332096c389d5ac6f6
|
|
| MD5 |
d59d614469041e3d3e0f4dd34c3009c2
|
|
| BLAKE2b-256 |
502af10b16804de861342470d88d1204c8ba5161c6cbbd8581f51b88f6d85137
|
File details
Details for the file devalerts-0.1.4-py3-none-any.whl.
File metadata
- Download URL: devalerts-0.1.4-py3-none-any.whl
- Upload date:
- Size: 12.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08e9751b5ec28f6ca8117ac196b70365d32ca59d933de0bfde4ab75fd74da16e
|
|
| MD5 |
1ae9bbfcd9efabbf6f67337c81168bd7
|
|
| BLAKE2b-256 |
f7b4b190084142ebac2a6687bdb3e26f2101e2ead1c31fdee445327de2e136f6
|