Sentry stopped receiving every controller exception, and a customer found it before any system did
A developer described this in an r/SaaS thread I started this week and it is the cleanest version of the problem I have seen.
Their Symfony app had an exception listener on kernel.exception registered at priority 200. It called setResponse(), which stopped propagation before Sentry's listener at priority 128 ever ran. Every controller exception in the application stopped reaching Sentry. Nothing crashed, nothing errored, and the dashboard stayed quiet. A quiet dashboard and a working dashboard render identically.
They found it because a person reported a 500 on a CSV import and the stack trace was not in Sentry at that timestamp. Not from a system. From a customer, hours later.
The detail I keep thinking about is the exception to it. Twelve Untrusted Host errors did reach Sentry in that same period, because on those the listener threw before it got as far as setResponse(). So the error reporting worked precisely when the suppressor was broken. Seeing errors arrive was never evidence the path was intact.
That is what separates this from an ordinary bug. Everything else in a system has something watching it. The watcher has nothing watching it, so when it fails, the observer and the failure share the same fault, and silence looks exactly like health.
The same shape caught me on Reddit. A comment of mine rendered at 1 point in my own logged in session while a moderation bot had already removed it. My own session was never an independent observer of my own visibility. Logged out, it did not exist.
The fix that generalises is not a better alert. It is a canary. Throw a deliberate exception through a real path and assert it arrives, so you are testing the reporter rather than the code. The same developer named the limit of that straight away: the canary only proves anything if its exception travels the same route a real one would.
What does your monitoring actually prove is alive, as opposed to reporting itself alive?
Replies
Same shape hit me on Vercel. A cron job that pinged an external API stayed green in the dashboard for three weeks straight. The ping was succeeding, the actual work after it was throwing and getting swallowed by a bare except. Green light, dead job.
What fixed it was not more logging. It was asserting on what the job produced rather than whether it ran. Every scheduled job across the portfolio now writes a row to a table with a timestamp and a status, and a separate check queries that table instead of trusting the runner's own exit code. Same logic as your canary, test the output, not the fact that something executed.
Your Reddit example is the sharpest bit of this. A logged in session was never independent proof of anything, since you are the one person guaranteed to still see your own content. Same trap catches uptime checks that hit a cached edge response instead of the origin.
The scary part is that “no alerts” and “monitoring is broken” look identical. I’d treat the monitoring path itself as a production dependency and continuously send a synthetic failure through the same real route.
If that canary doesn’t show up in Sentry within a defined window, that should trigger a completely separate alerting channel.
This is the same failure I used to see in reconciliation controls before I moved into analytics. A control that alerts on breach looks identical to one that has stopped running, right up until someone checks the underlying feed count rather than the alert count. Zero alerts got read as zero problems for months in more than one place I worked, because nobody was checking whether the control itself was still firing.
The canary idea is right, but the harder part in practice is getting time allocated to prove a negative. Nobody schedules a project around confirming that nothing is wrong, so it tends to get built only after an incident like this one, which is backwards.
Honest answer to your question: almost nothing I have running proves it is alive rather than reporting itself alive. Do you run the canary on a schedule now, or was this a one off fix after the Symfony case?