Client Reports
SDK self-reporting protocol for tracking discarded events and their discard reasons.
Client reports are a protocol feature that let SDKs send status reports about themselves to Sentry. They are primarily used to emit outcomes for events that were never sent — providing visibility into what is happening on the SDK side that affects the user experience.
Not to be confused with User Feedback.
Due to a bug in Relay which discards envelopes containing unknown envelope items, the minimum required Sentry version for client reports is 21.9.0.
Related specs:
- Envelopes — transport format
- Envelope Items — envelope item types
- Telemetry Processor — SDK processing architecture
- Rate Limiting — data category definitions
An outcome describes the fate of a telemetry item — whether it was accepted, filtered, rate-limited, or discarded. Client reports cover the discarded category: items that were never sent to Sentry.
A discard reason is a string that explains why a telemetry item was dropped. Each discarded item is categorized by reason and data category.
Data categories (e.g., error, transaction, span) identify the type of telemetry item that was discarded. These are the same categories used for rate limits.
Client reports are sent as envelope items to Sentry, typically as separate envelopes or with one of the already scheduled envelopes. Their main purpose is to bring visibility into what is happening on the SDK side.
SDKs might drop events in several places — transport queue overflow, rate limit backoff, sampling, before_send, event processors — and this loss can be invisible to a customer. Client reports let an SDK emit such event outcomes to provide data about how often this is happening.
The party that drops an envelope item MUST record and report it.
SDKs can assume client reports are never rate limited (since 1.9.0). The server minimizes the possibility of client reports getting rate limited, but the SDKs shouldn't worry about this edge case as this feature is best-effort.
Bugs in SDKs are out of scope for client reports and are not tracked using client reports (since 1.7.0).
Client reports do not expect 100 percent correct numbers, and it is acceptable for SDKs to lose a small number of client reports. The expectation is to give users an approximation of specific outcomes.
When SDKs receive an HTTP 2xx status code response from Sentry, they MUST consider it a successful send. No client report is recorded.
If Sentry returns an HTTP 4xx or HTTP 5xx status code, SDKs:
- MUST discard the envelope.
- MUST record a client report with the discard reason
send_error. ForHTTP 429andHTTP 413, follow the dedicated sections below instead, as they have additional requirements.
For an HTTP 413 Content Too Large response, SDKs:
- MUST discard the envelope and record a client report with the discard reason
send_error. Upstream usually records a client report for oversized envelopes, but not always. Double-counting is preferable to not counting, so users are aware of all dropped envelopes. Since client reports are not expected to be fully accurate, we accept this tradeoff for now. - MUST NOT retry sending the envelope.
- SHOULD log an error, informing users that the envelope was discarded due to size limits.
- MAY add information from the response body to the logged error. If doing so, SDKs MUST be aware that Relay can change or remove information in the response body for an
HTTP 413at any time without notice.
For an HTTP 429 Too Many Requests response, SDKs:
- MUST respect the rate limiting rules, such as correctly parsing the retry-header.
- MUST discard the envelope, but MUST NOT record a client report, because the upstream already does this. Doing this would double-count client reports for discarded envelopes.
- MUST NOT retry sending the envelope.
When failures occur that are caused by processing in the SDK itself, SDKs MUST discard the envelope and record a client report with the discard reason internal_sdk_error.
SDKs MAY retry sending the envelope when a network error occurs (connection timeout, DNS resolution failure, connection reset by peer). No client report is required for retried network errors.
When a transaction is dropped, SDKs MUST record an additional span category entry containing the number of spans inside the transaction plus one. The plus one stems from the fact that Relay extracts an additional span from the transaction. If a transaction contains no spans, SDKs still report one dropped transaction and one dropped span. This also applies to transactions that are not sampled.
If certain spans are dropped in beforeSendTransaction, an event processor, etc., SDKs also report those.
When a log is dropped, SDKs MUST record an additional log_byte category entry containing the byte size of the log. This provides visibility into the volume of log data being discarded, complementing the item count reported under the log_item category.
Since client reports use a (reason, category) → quantity map for aggregation, byte sizes are tracked as a separate category rather than an extra field. Each dropped log produces two entries in the map: one incrementing the log_item count by 1, and one incrementing the log_byte count by the log's byte size.
The byte size does not need to be exact. SDKs MAY use an approximation, such as the in-memory size of the log object or the size of its serialized representation. The goal is to provide a useful signal, not a precise measurement.
SDKs SHOULD use a single, centralized ClientReports component per client that handles aggregation internally:
- MUST be scoped per client — if an SDK supports multiple client instances, each MUST have its own
ClientReportsinstance. This is required because clients are already scoped per DSN, so scopingClientReportsper client ensures report data is attributed to the correct DSN and avoids mixing data across instances. - SHOULD be accessible throughout the SDK, so any component that drops telemetry can record into it.
- SHOULD use a no-op implementation when client reports are disabled via the
sendClientReportsoption, to avoid overhead. - SHOULD maintain a map where the key is a
reason-categorystring and the value is the number of dropped items. Components call aRecordmethod to increment counters directly. - MUST be thread safe for concurrent read and write access, since multiple SDK components record into it simultaneously.
- MUST atomically read and reset all counters during extraction to prevent double-counting.
The original specification suggested tracking client reports directly within the transport. However, due to the increase in telemetry data types and the possibility of data loss at various stages in the SDK, this approach no longer scales well. Client reports must be accessible across different parts of the SDK. Thus, we now recommend developing a separate ClientReports component rather than integrating it into the transport.
SDKs SHOULD own the ClientReports component at the client level, regardless of whether they implement the full telemetry processor architecture. This keeps the transport focused on delivery.
Once SDKs implement the telemetry processor, the client SHOULD NOT forward reports to the transport directly. Instead, the telemetry processor SHOULD be responsible for extracting the aggregated report and attaching it to outgoing envelopes or sending it as a standalone envelope.
Client reports are separate from the normal telemetry data flow — they do not go through buffers or queues. See the telemetry processor client reports section for the full architecture diagram and recording points.
It is not required, for example:
- to persist the data when an application crashes.
- to move an envelope item with a client report to the next envelope when the cache for envelopes is full.
SDKs SHOULD minimize unnecessary HTTP requests and MUST NOT send an envelope for each discarded event. Adjust these recommendations as needed:
- For low-frequency apps (mobile/web): Attach discarded events to scheduled envelopes. Fewer client reports are acceptable in such cases.
- For high-frequency apps (backends): Periodically flush discarded events or attach to scheduled envelopes.
SDKs SHOULD provide a way to turn sending of client reports on and off. This option is called send_client_reports or sendClientReports.
For SDKs still sending legacy events instead of envelopes for backward compatibility with older Sentry servers, the recommendation is to send the client report as a separate envelope or attach it to pending session envelopes.
There is no expectation that such bookkeeping can work transparently for custom transports. Consequently, it's acceptable if client reports are optional for custom transports.
Item type "client_report" contains a client report payload encoded in JSON.
Constraints:
- This Item MAY occur multiple times per Envelope, but SDKs SHOULD avoid sending more client reports than necessary.
- This Item can either be included in an Envelope with other Items, or it MAY be sent by itself.
- No envelope headers are required.
- Size limit: 4 KiB (see Envelopes — Size Limits).
A client report consists of a JSON payload with the following fields:
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
timestamp | String or Number | OPTIONAL | 1.0.0 | The timestamp of when the client report was created. Must be an ISO DateTime string or a UNIX timestamp. If not sent, the server assumes the current UTC timestamp. In the data model, this is called received. |
discarded_events | Array | REQUIRED | 1.0.0 | List of outcome objects {reason, category, quantity}. |
Each outcome object in discarded_events has:
| Field | Type | Required | Description |
|---|---|---|---|
reason | String | REQUIRED | A discard reason that defines why events were lost (see Discard Reasons). |
category | String | REQUIRED | The data category for which the discard reason applies (since 1.2.0). |
quantity | Number | REQUIRED | The number of events which were lost. |
The following discard reasons are defined for discarded_events:
| Reason | Since | Description |
|---|---|---|
queue_overflow | 1.0.0 | SDK internal queue (e.g., transport queue) overflowed. |
cache_overflow | 1.0.0 | SDK internal cache (e.g., offline event cache) overflowed. |
ratelimit_backoff | 1.0.0 | Rate limit instructed the SDK to back off. |
network_error | 1.0.0 | Network errors, not retried. |
sample_rate | 1.0.0 | Configured sample rate. |
before_send | 1.0.0 | Dropped in before_send. |
event_processor | 1.0.0 | Dropped by event processor; may also be used for ignored exceptions/errors (since 1.6.0). |
send_error | 1.11.0 | Error when sending (e.g., 400 response). |
internal_sdk_error | 1.11.0 | Internal SDK error (e.g., web worker crash). |
insufficient_data | 1.12.0 | Lack of data in the event (e.g., not enough samples in a profile). |
backpressure | 1.13.0 | Downsampling due to system load. |
buffer_overflow | 1.15.0 | SDK internal buffer (e.g., breadcrumbs buffer) overflowed. |
ignored | 1.16.0 | Telemetry item was ignored by the SDK (e.g., a span was ignored by ignore_spans; can also be used by other deny-list mechanisms). |
invalid | 1.17.0 | Failed validation (e.g., replay session exceeded maximum allowed length). |
In case a reason needs to be added, it also has to be added to the allowlist in snuba.
The following outcome types are reserved for relay use:
rate_limited_events, filtered_events, filtered_sampling_events
These function like discarded_events (same {reason, category, quantity} structure) but identify events that were rate limited, filtered, or filtered by dynamic sampling at a relay. Client SDKs MUST NOT emit these unless they are operating as a relay. The reason codes for these need to match the reason codes that relay would emit directly to Sentry.
{ "timestamp": "2020-02-07T14:16:00Z", "discarded_events": [ { "reason": "queue_overflow", "category": "error", "quantity": 23 }, { "reason": "queue_overflow", "category": "transaction", "quantity": 1321 } ] } {} {"type":"client_report"} {"timestamp":"2020-02-07T14:16:00Z","discarded_events":[{"reason":"queue_overflow","category":"error","quantity":23}]} When a transaction with 2 spans is dropped due to queue overflow:
{ "discarded_events": [ { "reason": "queue_overflow", "category": "transaction", "quantity": 1 }, { "reason": "queue_overflow", "category": "span", "quantity": 3 } ] } The span quantity is 3 (2 spans + 1 for the transaction itself, since Relay extracts an additional span from the transaction).
When 5 logs totaling approximately 12,400 bytes are dropped due to buffer overflow:
{ "discarded_events": [ { "reason": "buffer_overflow", "category": "log_item", "quantity": 5 }, { "reason": "buffer_overflow", "category": "log_byte", "quantity": 12400 } ] } The log_item entry counts the number of dropped logs, while the log_byte entry tracks the total byte size of those logs.
| Version | Date | Summary |
|---|---|---|
1.21.0 | 2026-03-06 | Added log byte outcomes for tracking discarded log data size |
1.20.0 | 2026-03-04 | Added architecture guidance |
1.19.0 | 2026-02-06 | Added network failure recording rules (send_error for 4xx/5xx, no report for 429) |
1.18.0 | 2026-01-29 | Added telemetry processor integration, updated SDK recommendations |
1.17.0 | 2026-01-21 | Added `invalid` discard reason |
1.16.0 | 2026-01-14 | Added `ignored` discard reason |
1.15.0 | 2025-01-21 | Added `buffer_overflow` discard reason |
1.14.0 | 2024-09-10 | Added span outcome tracking (span category for dropped transactions) |
1.13.0 | 2023-12-12 | Added `backpressure` discard reason |
1.12.0 | 2023-06-26 | Added `insufficient_data` discard reason |
1.11.0 | 2023-02-09 | Added `send_error` and `internal_sdk_error` discard reasons |
1.10.0 | 2022-06-23 | Added product context (Scope and Intent) |
1.9.0 | 2022-05-06 | Client reports never rate limited by server |
1.8.0 | 2022-04-11 | Added HTTP status code handling guidance |
1.7.0 | 2022-04-06 | SDK bugs out of scope for client reports |
1.6.0 | 2022-04-04 | event_processor discard for ignored exceptions |
1.5.0 | 2022-04-04 | Added `send_client_reports` configuration option |
1.4.0 | 2022-03-28 | SDK recommendations, legacy events, custom transports sections |
1.3.0 | 2022-03-28 | No double counting — MUST NOT record for HTTP 429 |
1.2.0 | 2022-03-22 | Specified data categories for discard reasons |
1.1.0 | 2021-12-24 | Relay outcome types (rate_limited_events, filtered_events, filtered_sampling_events) |
1.0.1 | 2021-09-10 | Fixed field name from discard_reason to reason |
1.0.0 | 2021-09-10 | Initial spec — client report protocol, envelope format, discard reasons |
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").