--- title: "Client Reports" description: "SDK self-reporting protocol for tracking discarded events and their discard reasons." url: https://develop.sentry.dev/sdk/telemetry/client-reports/ --- # Client Reports This document uses key words such as "MUST", "SHOULD", and "MAY" as defined in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) to indicate requirement levels. Statusstable Version`1.21.0`[(changelog)](https://develop.sentry.dev/sdk/telemetry/client-reports.md#changelog) ## [Overview](https://develop.sentry.dev/sdk/telemetry/client-reports.md#overview) 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](https://docs.sentry.io/product/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](https://github.com/getsentry/relay/blob/master/CHANGELOG.md#2190). Related specs: * [Envelopes](https://develop.sentry.dev/sdk/foundations/transport/envelopes.md) — transport format * [Envelope Items](https://develop.sentry.dev/sdk/foundations/transport/envelope-items.md) — envelope item types * [Telemetry Processor](https://develop.sentry.dev/sdk/foundations/processing/telemetry-processor.md) — SDK processing architecture * [Rate Limiting](https://develop.sentry.dev/sdk/foundations/transport/rate-limiting.md) — data category definitions *** ## [Concepts](https://develop.sentry.dev/sdk/telemetry/client-reports.md#concepts) ### [Outcomes](https://develop.sentry.dev/sdk/telemetry/client-reports.md#outcomes) 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. ### [Discard Reasons](https://develop.sentry.dev/sdk/telemetry/client-reports.md#discard-reasons) A **discard reason** is a string that explains why a telemetry item was dropped. Each discarded item is categorized by reason and [data category](https://develop.sentry.dev/sdk/foundations/transport/rate-limiting.md#definitions). ### [Data Categories](https://develop.sentry.dev/sdk/telemetry/client-reports.md#data-categories) 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](https://develop.sentry.dev/sdk/foundations/transport/rate-limiting.md#definitions). *** ## [Behavior](https://develop.sentry.dev/sdk/telemetry/client-reports.md#behavior) ### [Core Operation](https://develop.sentry.dev/sdk/telemetry/client-reports.md#core-operation) Stablespecified since 1.0.0 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. ### [Network Failure Recording](https://develop.sentry.dev/sdk/telemetry/client-reports.md#network-failure-recording) Stablespecified since 1.8.0 #### [Successful Sends (HTTP 2xx)](https://develop.sentry.dev/sdk/telemetry/client-reports.md#successful-sends-http-2xx) When SDKs receive an `HTTP 2xx` status code response from Sentry, they **MUST** consider it a successful send. No client report is recorded. #### [Error Responses (HTTP 4xx/5xx)](https://develop.sentry.dev/sdk/telemetry/client-reports.md#error-responses-http-4xx5xx) Stablespecified since 1.8.0 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`. For `HTTP 429` and `HTTP 413`, follow the dedicated sections [below](https://develop.sentry.dev/sdk/telemetry/client-reports.md#network-429) instead, as they have additional requirements. #### [Content Too Large (HTTP 413)](https://develop.sentry.dev/sdk/telemetry/client-reports.md#content-too-large-http-413) Stablespecified since 1.19.0 For an [`HTTP 413 Content Too Large`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413) 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 413` at any time without notice. #### [Rate Limited (HTTP 429)](https://develop.sentry.dev/sdk/telemetry/client-reports.md#rate-limited-http-429) Stablespecified since 1.3.0 For an [`HTTP 429 Too Many Requests`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) response, SDKs: * **MUST** respect the [rate limiting rules](https://develop.sentry.dev/sdk/foundations/transport/rate-limiting.md), 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. #### [Processing Failures](https://develop.sentry.dev/sdk/telemetry/client-reports.md#processing-failures) Stablespecified since 1.11.0 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`. #### [Transport Errors](https://develop.sentry.dev/sdk/telemetry/client-reports.md#transport-errors) Stablespecified since 1.8.0 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. ### [Span Outcomes](https://develop.sentry.dev/sdk/telemetry/client-reports.md#span-outcomes) Stablespecified since 1.14.0 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. ### [Log Byte Outcomes](https://develop.sentry.dev/sdk/telemetry/client-reports.md#log-byte-outcomes) Stablespecified since 1.21.0 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. ### [Aggregation](https://develop.sentry.dev/sdk/telemetry/client-reports.md#aggregation) Candidatespecified since 1.20.0 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 `ClientReports` instance. This is required because clients are already scoped per DSN, so scoping `ClientReports` per 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 `sendClientReports` option, to avoid overhead. * **SHOULD** maintain a map where the key is a `reason-category` string and the value is the number of dropped items. Components call a `Record` method 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. ### [Telemetry Processor Integration](https://develop.sentry.dev/sdk/telemetry/client-reports.md#telemetry-processor-integration) Candidatespecified since 1.20.0 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](https://develop.sentry.dev/sdk/foundations/processing/telemetry-processor.md) architecture. This keeps the transport focused on delivery. Once SDKs implement the [telemetry processor](https://develop.sentry.dev/sdk/foundations/processing/telemetry-processor.md), 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](https://develop.sentry.dev/sdk/foundations/processing/telemetry-processor.md#client-reports) for the full architecture diagram and recording points. ### [SDK Recommendations](https://develop.sentry.dev/sdk/telemetry/client-reports.md#sdk-recommendations) Stablespecified since 1.4.0 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: 1. For low-frequency apps (mobile/web): Attach discarded events to scheduled envelopes. Fewer client reports are acceptable in such cases. 2. For high-frequency apps (backends): Periodically flush discarded events or attach to scheduled envelopes. ### [Configuration](https://develop.sentry.dev/sdk/telemetry/client-reports.md#configuration) Stablespecified since 1.5.0 SDKs **SHOULD** provide a way to turn sending of client reports on and off. This option is called `send_client_reports` or `sendClientReports`. ### [Legacy Events](https://develop.sentry.dev/sdk/telemetry/client-reports.md#legacy-events) Stablespecified since 1.4.0 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. ### [Custom Transports](https://develop.sentry.dev/sdk/telemetry/client-reports.md#custom-transports) Stablespecified since 1.4.0 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. *** ## [Wire Format](https://develop.sentry.dev/sdk/telemetry/client-reports.md#wire-format) ### [Envelope Item](https://develop.sentry.dev/sdk/telemetry/client-reports.md#envelope-item) Stablespecified since 1.0.0 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](https://develop.sentry.dev/sdk/foundations/transport/envelopes.md)). ### [Payload Structure](https://develop.sentry.dev/sdk/telemetry/client-reports.md#payload-structure) Stablespecified since 1.0.0 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](https://develop.sentry.dev/sdk/telemetry/client-reports.md#discard-reasons)). | | `category` | String | **REQUIRED** | The [data category](https://develop.sentry.dev/sdk/foundations/transport/rate-limiting.md#definitions) for which the discard reason applies (since 1.2.0). | | `quantity` | Number | **REQUIRED** | The number of events which were lost. | ### [Discard Reasons](https://develop.sentry.dev/sdk/telemetry/client-reports.md#discard-reasons-1) Stablespecified since 1.0.0 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](https://github.com/getsentry/snuba/blob/master/rust_snuba/src/processors/outcomes.rs#L15). ### [Relay Outcome Types](https://develop.sentry.dev/sdk/telemetry/client-reports.md#relay-outcome-types) Stablespecified since 1.1.0 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. *** ## [Examples](https://develop.sentry.dev/sdk/telemetry/client-reports.md#examples) ### [Wire Format](https://develop.sentry.dev/sdk/telemetry/client-reports.md#wire-format-1) ```json { "timestamp": "2020-02-07T14:16:00Z", "discarded_events": [ { "reason": "queue_overflow", "category": "error", "quantity": 23 }, { "reason": "queue_overflow", "category": "transaction", "quantity": 1321 } ] } ``` ### [Full Envelope](https://develop.sentry.dev/sdk/telemetry/client-reports.md#full-envelope) ```json {} {"type":"client_report"} {"timestamp":"2020-02-07T14:16:00Z","discarded_events":[{"reason":"queue_overflow","category":"error","quantity":23}]} ``` ### [Span Outcome Example](https://develop.sentry.dev/sdk/telemetry/client-reports.md#span-outcome-example) When a transaction with 2 spans is dropped due to queue overflow: ```json { "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). ### [Log Byte Outcome Example](https://develop.sentry.dev/sdk/telemetry/client-reports.md#log-byte-outcome-example) When 5 logs totaling approximately 12,400 bytes are dropped due to buffer overflow: ```json { "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. *** ## [Changelog](https://develop.sentry.dev/sdk/telemetry/client-reports.md#changelog) | 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 |