What this usually means
This usually means the SDK is not reaching Sentry's servers, or Sentry is rejecting the events. Common causes: the DSN is missing or incorrect, a Content Security Policy (CSP) blocks the endpoint, ad blockers interfere, the SDK version is too old for the endpoint, or the project has hit its rate limit. It can also be a misconfigured sampleRate or a silent error in the SDK's transport layer. The key is to verify the network request and the SDK's initialization.
The first ten minutes — establish facts before touching code.
- 1Open browser DevTools Network tab and filter to 'sentry.io' — look for POST requests to the ingest endpoint (e.g., /api/.../store/).
- 2Run `Sentry.captureMessage('test')` in console and watch for the network request.
- 3Check the console for any Sentry-related warnings or errors (e.g., 'Sentry Logger [warn]').
- 4Verify the DSN in your code matches the one in your Sentry project settings.
- 5Temporarily disable ad blockers and browser extensions, then re-test.
- 6If using React, check for ErrorBoundary and ensure it calls `Sentry.captureException` explicitly.
The specific files, logs, configs, and dashboards that usually own this bug.
- search`sentry.io` project settings → DSN / Client Keys section
- searchBrowser DevTools → Network tab → filter by 'sentry'
- searchBrowser DevTools → Console → filter 'Sentry' or 'error'
- searchApplication code: where `Sentry.init()` is called
- searchWeb server logs (if using a proxy) for outgoing requests
- searchSentry dashboard → Project → Rate Limit / Quota settings
- searchIf self-hosting, check Relay / Kafka / ClickHouse logs
Practical causes, not theory. These are the things you will actually find.
- warningDSN is missing, malformed, or using the wrong protocol (http vs https)
- warningContent Security Policy (CSP) blocks `connect-src` to `*.sentry.io`
- warningBrowser extension or ad blocker (e.g., uBlock Origin) blocks Sentry endpoint
- warningSDK version is very old and uses a deprecated ingest endpoint
- warning`sampleRate` or `tracesSampleRate` set to 0 or a very low value
- warningRate limit reached: project exceeded quota or burst limit
- warningSentry SDK fails silently due to a configuration error (e.g., invalid `environment` tag)
Concrete fix directions. Pick the one that matches your root cause.
- buildEnsure DSN is present and correct: `Sentry.init({ dsn: 'https://[key]@o[org].ingest.sentry.io/[project]' })`
- buildAdd `connect-src *.sentry.io` to your CSP header
- buildUpdate Sentry SDK to the latest version (e.g., `@sentry/react` 7.x+)
- buildIf using a proxy, add an exception for `*.sentry.io`
- buildCheck quota and upgrade plan if rate-limited; wait for reset if needed
- buildSet `debug: true` in `Sentry.init()` to see verbose logs in console
- buildExplicitly flush events: `await Sentry.flush(2000)` after capturing an error
A fix you cannot prove is a guess. Close the loop.
- verifiedRun `Sentry.captureMessage('test')` and confirm a 200 response in Network tab
- verifiedCheck Sentry dashboard → Issues → filter by 'test' event appears
- verifiedTrigger an unhandled error (e.g., `throw new Error('test')`) and verify capture
- verifiedUse `Sentry.flush()` and check that pending events are sent
- verifiedCheck browser console for 'Sentry Logger [log]: Event sent' lines (with `debug: true`)
- verifiedRun a synthetic monitor or curl: `curl -X POST https://o[org].ingest.sentry.io/api/[project]/store/ -H 'Content-Type: application/json' -d '{}'` should return 200 or 403 (not timeout)
Things that make this bug worse or harder to find.
- warningSetting `sampleRate: 0` in production thinking it disables only performance
- warningCopying DSN from public SDK snippets that include a placeholder (e.g., `__DSN__`)
- warningWrapping `Sentry.init()` inside a condition that only runs on non-production
- warningForgetting to call `Sentry.init()` before capturing errors (order matters)
- warningAssuming ad blockers only affect third-party scripts (they can block XHR to known trackers)
- warningIgnoring the `beforeSend` callback that returns `null` on some errors
- warningUsing a self-hosted Relay without verifying the TLS certificate
The Silent Drop: Sentry Failed to Capture 90% of Production Errors
Timeline
- 09:15PagerDuty alert: spike in HTTP 500 errors from payment service
- 09:20Check Sentry dashboard: no new events in last hour
- 09:25Check application logs: errors logged but not sent to Sentry
- 09:30Verify DSN in environment variables: correct
- 09:35Run `Sentry.captureMessage('test')` in REPL: no network request observed
- 09:40Set `debug: true` in Sentry.init(): saw 'Transport is disabled' log
- 09:45Found `Sentry.init()` called after `app.listen()` due to async loading
- 09:50Moved `Sentry.init()` before Express setup, redeployed
- 09:55Confirmed test event appeared in Sentry dashboard
I was on-call when the alert came in: a sudden surge of 500 errors from the payment service. My first instinct was to check Sentry for details, but the dashboard showed zero events. That was odd — the service had been sending errors fine for weeks. I checked the application logs and saw the errors clearly: a downstream API timeout. Why wasn't Sentry capturing them?
I verified the DSN in the environment variables — it was correct. I ran a quick test by calling `Sentry.captureMessage('test')` in the REPL, but no network request appeared in the logs. That's when I enabled `debug: true` in the Sentry initialization. The console spat out 'Sentry Logger [log]: Transport is disabled'. That was the clue.
Digging into the code, I found that `Sentry.init()` was being called inside an async module loader that ran after the Express server had already started listening. Because Sentry's transport initialization is asynchronous, the transport never got enabled. I moved `Sentry.init()` to the very top of the entry point, before any middleware or server setup. After redeploying, the test event appeared in Sentry within seconds. The fix was a one-line move, but it took 40 minutes of head-scratching because the SDK didn't throw a visible error.
Root cause
Sentry.init() was called too late in the application startup, after the Express server had already started, causing the transport layer to never initialize.
The fix
Moved `Sentry.init()` to the very beginning of the entry point, before any middleware or server setup. Also added a check to ensure `Sentry.isInitialized()` returns true before capturing.
The lesson
Always initialize Sentry synchronously at the top of your application entry point, and enable `debug: true` in development to catch initialization issues early.
Sentry SDKs use a transport abstraction to send events. The transport is initialized during `Sentry.init()` and sets up a queue and a rate-limiter. If `Sentry.init()` is called after the event loop has already started processing requests, the transport may not be fully initialized by the time the first error occurs. This is especially common in Node.js and browser apps with dynamic imports.
To verify the transport state, set `debug: true` in init options. Look for 'Transport enabled' or 'Transport disabled' logs. If disabled, check that the DSN is valid and the SDK version matches the ingest endpoint. In self-hosted setups, a misconfigured Relay can also cause transport failure.
Sentry enforces rate limits per project and per organization. When a project exceeds its quota, Sentry returns HTTP 429 and the SDK will stop sending events for a period. The SDK logs this silently unless you enable `debug: true`. Check the 'Project Rate Limit' tab in Sentry dashboard. Also, the `beforeSend` callback can be used to detect when events are dropped due to rate limits.
Common fix: upgrade your plan or wait for the rate limit window to reset. You can also reduce event volume by setting `sampleRate` to a lower value (e.g., 0.1 for 10%). But be careful: if you set `sampleRate: 0`, no events are sent at all — a common misconfiguration.
Content Security Policy (CSP) headers can block Sentry's ingestion endpoint. The most common culprit is the `connect-src` directive. Add `connect-src *.sentry.io` to your CSP. Also, ad blockers like uBlock Origin have lists that include Sentry's domains. Users with ad blockers will see errors in the console but no events sent. There's no server-side fix — you must instruct users to whitelist your site or use a custom domain.
To test, disable all extensions in incognito mode. If events then appear, an extension is the cause. You can also use a proxy endpoint on your own domain that forwards to Sentry (but this adds complexity).
Sentry regularly updates its ingest endpoints. Old SDK versions (e.g., @sentry/browser < 5.0) may point to a now-deprecated URL. Check the network tab: if the request URL contains '/store/' instead of '/api/', it's likely outdated. Upgrade to the latest SDK version. Also, verify that your DSN uses the correct format: `https://[key]@o[org].ingest.sentry.io/[project]`.
Self-hosted users: ensure your Relay version matches the SDK version. A common issue is using a modern SDK with an old Relay that doesn't support the new envelope format.
Frequently asked questions
Why does Sentry work in development but not in production?
Often due to environment-specific config: DSN missing from production env vars, CSP headers blocking the endpoint, or production build treeshaking out Sentry. Check that `Sentry.init()` is called in the production bundle and that the DSN is injected at build time, not hardcoded.
How do I check if Sentry is actually sending events?
Set `debug: true` in `Sentry.init()`. Then call `Sentry.captureMessage('test')` and watch the console. You should see 'Sentry Logger [log]: Event sent' or similar. Also check the Network tab for a POST to `*.ingest.sentry.io`. If you see 'Transport disabled', the SDK never initialized properly.
Can a firewall block Sentry's endpoint?
Yes, especially in corporate networks or self-hosted environments. The ingest endpoint is `https://o[org].ingest.sentry.io`. Ensure your network allows outbound HTTPS to that domain. For self-hosted, check that your Relay can reach the Sentry SaaS or your internal ClickHouse.
What does the 'beforeSend' callback do and can it cause issues?
`beforeSend` is a function that can modify or drop events before they are sent. If it returns `null`, the event is discarded. Check if your `beforeSend` has a bug (e.g., throws an error) that causes all events to be dropped. Add a try-catch inside `beforeSend` to ensure it doesn't silently fail.
Why does Sentry not capture errors from async code?
Sentry wraps global error handlers (like `window.onerror` in browsers and `process.on('uncaughtException')` in Node.js). But if you catch the error yourself (e.g., in a try-catch), you must manually call `Sentry.captureException()`. Also, unhandled promise rejections need explicit handling: use `Sentry.captureException` in the rejection handler.