Troubleshooting
By symptom rather than by subsystem. Two screens answer most of this: the usage screen says what was refused and why, and an issue's own frames say which artifact they wanted.
Nothing is appearing at all
The most common cause by a distance. In Flutter, initialization has to wrap runApp through appRunner, or nothing installs the error handlers. In Next.js, the client config is loaded by the framework and the server config is not: it is imported from instrumentation.ts, and register has to await it.
That is a specific and very recognisable failure, and it means instrumentation.ts is missing, is not exporting onRequestError = Sentry.captureRequestError, or is not importing the server config for the nodejs runtime. Server components, route handlers and middleware all report through that hook. The Next.js quick start has the file in full.
Ingest answers 401 with the reason in an x-sentry-error header, and the usage screen counts it under unauthorized. If that number is climbing, the DSN in the build is not the DSN on the key card. A revoked key stops working on the next request rather than the next deploy, so a build that worked yesterday can be refused today.
A beforeSend that returns null, an ignoreErrors entry, or a gate that disables the SDK outside production. All three are invisible from this end: an event that was never sent looks exactly like an app that never threw.
Transactions and log lines are stored, but they land on the Traces and Logs screens rather than in the issue list, so an app with tracing on and nothing throwing looks exactly like an app that is not reporting. Profiles and replays are acknowledged so your SDK does not retry them and then dropped, which shows on the usage screen as unsupported_item. Throw a real error before concluding anything from an empty issue list.
This bypasses your app entirely. Both values come out of the DSN, which is https://<public key>@monitor.unrealops.ai/ingest/<project>. A 200 with an id means the project, the key and the network are all fine and the problem is in the app. A 401 names itself in the x-sentry-error header.
curl -i -X POST \
"https://monitor.unrealops.ai/ingest/api/<project>/store/?sentry_key=<public key>" \
-H "content-type: application/json" \
-d '{"message":"hello from curl","level":"error"}'Events arrive but the stacks are unreadable
It is off until somebody turns it on. Frames still appear, marked unsymbolicated. The switch is on the project's Monitor settings screen.
Flutter: without --split-debug-info there is no symbol file, and if the build was also obfuscated the names are gone for good. Next.js: without productionBrowserSourceMaps: true the browser half of the build emits no maps at all, so the half your users hit has nothing behind it.
A frame that wanted an artifact says so and names the one it wanted, so the answer is usually on the frame itself. Check the build log: the uploader exits non zero when an upload fails, precisely so a build cannot quietly ship without symbols, and --dry-run lists what it would have found.
A frame is matched to a map by release. On Next.js the usual version of this is that server events carry a release, because the SDK detects the commit sha by itself, while browser events carry none, because a bundle cannot read process.env unless the value was inlined at build time. Uploading with no --release at all has the same effect from the other side, and the uploader warns about it.
Symbol storage is a pool, and when it fills the oldest release's indexes are deleted so the newest release's fit. The build in production always resolves; a build from a while ago can stop. Every upload response names the releases it evicted, so the fact is in the CI log, and the settings screen says how many releases this project is currently holding. Indexes are also swept after 90 days without a crash on that build.
A server side Next.js frame names a chunk
That is the default. Browser maps and server maps are two switches, and the second one is off until you ask for it, because it spends your own pooled symbol allowance on a half of the build not every project needs read: measured on a real Next.js 16 build, about 2.2 MB a release against the browser half's 0.8 MB.
Turn on Upload the server half of the build too under Symbolication. Nothing in your repository changes and no command changes: the uploader asks the project which halves it wants before it walks anything, so it takes effect on your next deploy.
Then it is one of the two causes above this section: the deploy that produced the crash did not upload, or its release does not match the release the maps went up under. The path itself is not the problem. A frame reported from /var/task/ is reduced to the same build relative path CI uploaded under, so a serverless host mounting the build somewhere else does not break the match.
My usage says things were refused
A refusal means Monitor would have stored something and did not, so data you expected is gone and something you can do about it exists. The other list is items that were never Monitor's to keep: they cost no quota, broke nothing, and no action follows. unsupported_item is the whole of the second list, and it counts up whenever an SDK is configured to send replays, profiles or streamed spans while nothing at all is wrong.
- monthly_cap. The month's ceiling was already reached. Ingest answers 429 with Retry-After, the SDK backs off and nothing is stored.
- spike. More events arrived in one minute than the per minute ceiling allows. This is what a crash loop looks like before the monthly budget would notice.
- unauthorized. The key in the DSN did not resolve to a live key on this project. Usually a revoked key or a DSN pasted from another project.
- payload_too_large. The body was over the maximum envelope size and was refused without being read.
- malformed. It could not be parsed as an envelope.
- not_in_plan. Sessions arrived and the plan does not include release health. They were acknowledged so the SDK does not retry them and they spent no event allowance.
- unsupported_item. A profile, a replay or a streamed span: an item type there is no product for here. Acknowledged, never stored, never charged. Turning replay or profiling off in the SDK stops it being sent at all. Transactions and log lines are not in this count, because they are stored; when one of those is refused it appears as monthly_cap or spike like anything else, measured against their own share of your allowance.
- internal. Storing it failed on our side. These are ours to fix, not yours.
A project can be refused because a sibling spent the month. The usage screen shows the pool, this project's share of it, and who else is drawing on it, which is the difference between a meter at eight per cent and a 429 you cannot explain. After a breach the project is refused cheaply for another thirty seconds, so a crash loop already in flight costs no database work at all.
My alerts are not arriving
This is how every project starts. Nothing is sent until an address is added under Alerts, because the platform does not guess at an address nobody gave it. The card's own badge says nobody is notified when that is the case.
Mute is a switch on the same card, and there is a minimum level under it: an alert only sends at that level and above. A spike is a property of the project's rate rather than of one event, so it is not filtered by the level.
From the issue list, three things send: a new issue, a regression, and an event spike. An issue you already have getting more events is none of those, and neither is a straggler from a build somebody already resolved the issue in. Uptime checks and scheduled jobs send on their own terms, and both are deliberately slow to speak: a check needs three consecutive failures before it is called down, and a job late but still inside its grace period is a state on the screen rather than a message.
A crash loop is often four hundred new issues in a minute rather than one issue four hundred times, so there are two ceilings. By default one issue may notify about the same thing once an hour, and the project may send at most ten emails an hour and forty a day. Nothing held back is lost: it is counted and named inside the next email that does go out, and the alerts card says how many are waiting right now and when the last one was sent.
Then no email can be sent regardless of what is configured, and the alerts card says so in a banner rather than leaving you to work it out. Slack and webhook delivery do not go through the mail provider and are unaffected. Use Send a test to settle it in one click: it reports what the mail provider actually said, and if it says it was sent, check the spam folder before changing anything.
There are three: email addresses you list, a Slack incoming webhook url if you paste one, and any https endpoint you register under Webhooks. They share one decision, so a mute or a ceiling silences all three together rather than one of them, and a project with only Slack configured still alerts. Each has a test that goes through the real signing and the real delivery, and the webhook endpoint keeps its last twenty deliveries so a receiver answering 4xx is visible rather than inferred.
Nothing here does that. There is no rota, no escalation policy and nothing that phones a person, so a webhook into whatever you already use for that is the whole of the answer this platform has.
If none of these is it
Two facts separate the two halves of almost every remaining question. The usage screen says whether we refused something and why, and it counts every refusal by reason rather than rounding them into a total. An issue's frames say whether a symbol lookup was tried and what it wanted. Between them, a silent DSN and a silently discarded event are different problems with different screens, and neither of them is a guess.