A clean Sentry inbox is a load-bearing developer tool. The day yours starts showing 4,000 events for things that aren't bugs, you stop opening it. The day after that, you miss the real bug.
We upgraded HoneyChat — Telegram-native AI companion, ~300 DAU — from Sentry SDK 1.x to 2.x and that's what happened. The SDK gained a useful new behavior: it auto-enables a set of integrations whenever it detects the relevant library imported. Loguru, OpenAI, SQLAlchemy, asyncpg, Redis, httpx — all on by default in 2.x. No more integrations=[...]
boilerplate.
This is great when those integrations capture only real errors. They don't.
HoneyChat backend stack (for context):
aiogram
Telegram polling bot (bot/main.py
)api/main.py
, uvicorn --workers 4
)llm
, images
, gifs
, voice
(workers/tasks.py
)gen_worker
(image/GIF generation queue)openai
Python SDK (base_url
swapped to https://openrouter.ai/api/v1
) — so openai.*
exception types fire on OpenRouter responses tooThat stack imports every library the Sentry 2.x auto-integration looks for. They all turn on.
Within a day of the upgrade:
logger.error("…")
from Loguruerror
-level openai.RateLimitError
and openai.APIConnectionError
tenacity
retries. Not bugs.asyncpg
/SQLAlchemy pool racebot
, api
, celery_worker
, nginx
back-to-back during a full release; pool reconnects produce a brief flurry of these. Not bugs.ConnectionResetError
Real bugs were drowning. Issue counts went from ~5/day to 4,000+.
Sentry SDK 2.x scans sys.modules
at init and turns on any integration whose target library is already imported. The relevant docs page lists them. Three matter most for a typical Python service:
LoguruIntegration
— captures Loguru records at ERROR
and above.OpenAIIntegration
— captures all openai.OpenAIError
raises (which, for us, includes everything OpenRouter ever returns through the OpenAI SDK).SqlalchemyIntegration
— captures slow queries, connection errors, and a few other states.You can disable individual integrations:
import sentry_sdk
from sentry_sdk.integrations.loguru import LoguruIntegration
from sentry_sdk.integrations.openai import OpenAIIntegration
sentry_sdk.init(
dsn=settings.SENTRY_DSN,
disabled_integrations=[
LoguruIntegration,
OpenAIIntegration,
],
)
…but that's a blunt instrument. We do want LLM errors reported when they're real. We want Loguru-routed errors reported when the line that produced them is actually a bug.
The fix is a before_send
filter.
core/sentry_filters.py
)
import sentry_sdk
from sentry_sdk.types import Event, Hint
EXPECTED_EXCEPTIONS = (
"openai.RateLimitError",
"openai.APIConnectionError",
"openai.APITimeoutError",
"openai.InternalServerError",
"redis.exceptions.ConnectionError",
"redis.exceptions.TimeoutError",
"asyncpg.exceptions.ConnectionDoesNotExistError",
"asyncpg.exceptions.InterfaceError",
"sqlalchemy.exc.OperationalError",
)
OPERATIONAL_LOGGERS = (
"core.llm", # fallback chain, content_filter rescue, retries
"core.image_gen", # GPU → API provider switchover
"core.voice", # Inworld TTS → gTTS fallback
"workers.gen_worker", # task-level fallback
)
def before_send(event: Event, hint: Hint) -> Event | None:
exc_info = hint.get("exc_info")
if exc_info:
exc_type = exc_info[0]
exc_path = f"{exc_type.__module__}.{exc_type.__name__}"
if exc_path in EXPECTED_EXCEPTIONS:
return None
logger_name = (event.get("logger") or "")
if logger_name in OPERATIONAL_LOGGERS:
level = event.get("level")
if level in ("error", "warning"):
return None
return event
sentry_sdk.init(
dsn=settings.SENTRY_DSN,
before_send=before_send,
traces_sample_rate=0.0,
profiles_sample_rate=0.0,
)
The filter is twenty lines. The two tuples are the actual contract: these exceptions and these loggers are noise. After deploying this, Sentry inbox went from 4,000+ to ~30 events/day. The 30 included two real bugs we'd been missing.
A filter is half the answer. The other half is fixing the log levels at the source. Our team now follows three rules:
logger.error(...)
logger.warning(...)
logger.info(...)
A normal-path "Gemini returned content_filter, falling back to Grok 4.20" is info
, not error
. The terminal might lose some red, but Sentry stops crying wolf.
When we audited our codebase against this, we found roughly 40 logger.error
lines in core/llm.py
, core/image_gen.py
and workers/gen_worker.py
that should have been warning
or info
. Fixing them at source means the before_send
filter doesn't need to grow indefinitely.
We considered ignore_errors=[...]
on sentry_sdk.init
instead of a before_send
. The problem is that ignore_errors
only matches by exception type name, not module path. ConnectionError
is ambiguous (Redis vs httpx vs asyncpg — all have one). The fully-qualified path check in before_send
is more precise.
We also considered turning the integrations off entirely. The risk is losing visibility into real LLM and DB errors — when there's a real bug in the LLM path, the OpenAIIntegration
's stack-trace enrichment is genuinely useful. Keeping the integrations on and filtering precisely was the better trade.
before_send
is the right hook for noise reduction.error
doesn't mean "Sentry-worthy", your Sentry inbox is broken.The hardest part of this work isn't the filter — it's getting the team to agree on what error
actually means.
This filter runs in production at ** HoneyChat** — Telegram-native AI companion bot. Canonical version:
— HoneyChat Engineering
disabled_integrations
.before_send
filteringNone
to drop, mutate to scrub.base_url
setup with OpenAI SDKopenai.*
exception types fire on OpenRouter responses.