> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# OpenTelemetry Overflow: Why Route Metrics Go Missing
- URL: https://debugly.dev/opentelemetry-metric-overflow-route-labels/
- Published: 2026-11-05T11:42:00.000Z
- Updated: 2026-11-05T11:42:00.000Z
- Description: An OpenTelemetry counter can keep its total after losing route detail. Find the overflow point, trace where labels disappeared, and verify the fix.
- Author: Rohit Bhadani
- Tags: Observability, Monitoring, Debugging

The request counter looks fine. The dashboard broken down by route, though, seems to have misplaced a chunk of traffic. Before you restart the Collector, inspect a raw point from the application's OpenTelemetry metrics SDK. Does it have `otel.metric.overflow=true` where you expected route and status attributes?

**That point is the SDK's overflow aggregate.** When the SDK can't create another aggregation for a distinct combination of metric attributes, it can put those measurements into one overflow point instead. For a synchronous counter, the count still contributes to the total at that aggregation boundary; the original breakdown doesn't. Fix the attributes before aggregation, then check the raw points again. A bigger limit can buy headroom, but it won't make a request ID a useful metric dimension.

*This is an AI-prepared, documentation-based explainer, not a personally reproduced incident. The walkthrough, values and verification steps are illustrative and were not executed. See Debugly's* [*editorial policy*](https://debugly.dev/editorial-policy/)*.*

## Start at the last place the labels could still exist

Suppose a checkout service records a synchronous request counter with a route template, a status code and a request ID. Those first two attributes help answer questions like “Are checkout failures rising?” The request ID gives each call its own combination. You now have a tracing problem wearing a metrics name badge.

It's reasonable to suspect a dashboard filter first. A query that requires a route label naturally excludes points that don't have one. But if the route is already absent in the SDK's output, no query can bring it back. Compare the same instrument, resource and collection interval at three points: the SDK output, the Collector output if you use one, and the backend query. Start with an unfiltered view; adding a route filter before looking for overflow can hide the very point you're looking for.

| Where the detail disappears                                                    | What to inspect next                                                                                                   |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| The SDK emits an overflow point without the expected attributes.               | Inspect the instrument's recorded attributes, matching Views and effective cardinality limit.                          |
| The SDK emits normal points, but those points or attributes change downstream. | Compare Collector processors, exporter translation and backend ingestion. Don't diagnose SDK overflow from this alone. |
| The backend has the detail, but the panel doesn't show it.                     | Inspect label selectors, grouping and the selected time window.                                                        |

If you can't see the SDK's output in production, inspect a local or staging instance with the same SDK version, reader and View configuration. Don't dump raw customer identifiers into a production debug exporter to find out whether you should have recorded them in the first place.

## The count can survive without its labels

The [OpenTelemetry Metrics SDK specification](https://opentelemetry.io/docs/specs/otel/metrics/sdk/?ref=debugly.dev#cardinality-limits) describes the limit in terms of distinct attribute combinations. It isn't a cap on the number of requests. A million increments with identical attributes can share one aggregation; many distinct IDs can require many aggregations. The synthetic overflow point carries the overflow marker as its only point attribute. Resource attributes, such as service identity, are a separate layer.

Consider an illustrative interval in which a single counter records 100 increments. Its normal points account for 80; its overflow point accounts for 20\. You can reconcile the 100 increments across the stream, but you can't distribute the 20 back to routes or status codes. A route panel may show only 80 increments, while a service total that includes the overflow point shows 100\. Both views can be faithfully reporting the data they received.

That arithmetic applies to aligned increments of an additive counter. Don't add raw cumulative snapshots across time or average the percentiles of multiple histograms. Compare like windows and account for resets when deriving changes from cumulative points. Also keep the scope of the guarantee straight: the specification requires each synchronous measurement to enter one aggregation during overflow. It doesn't guarantee that a Collector, exporter or backend will deliver every point.

Temporality changes what the missing detail can look like. With cumulative synchronous aggregation, the specification preserves the attribute sets seen before overflow began. With delta aggregation, it permits the SDK to choose a subset. An old route continuing to appear while a new combination disappears is therefore a useful clue for cumulative output, but you shouldn't assume every SDK keeps the same winners across intervals.

## Reduce the combinations before they need a slot

First, check what is actually recorded. In the illustrative service, taking out `request.id` preserves the useful route and status dimensions. If the problem is a raw path such as `/orders/482913`, record the router's bounded route template instead. The [HTTP metrics conventions](https://opentelemetry.io/docs/specs/semconv/http/http-metrics/?ref=debugly.dev) specifically rule out using the raw URI path as a substitute for `http.route` when a framework doesn't supply routing information.

If shared instrumentation supplies attributes you don't need, configure a matching SDK View to retain a small allowlist. Apply it before the cardinality limit. Dropping the field in a Collector after the SDK has aggregated may help downstream costs, but the Collector can't reconstruct a route that was already folded into overflow.

For a concrete configuration check, the [OpenTelemetry Go metrics SDK v1.47.0](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric@v1.47.0?ref=debugly.dev#WithCardinalityLimit) documents a provider default of 2,000 and a reader override by instrument kind. That's a Go SDK example, not a universal value to assume across versions or languages. Read the effective configuration of the reader that feeds the panel. A local debug reader and a production exporter may not have identical settings.

Keep high-detail investigation data in appropriately sampled traces or governed logs, with your normal redaction and retention rules. That gives you a place to look up a particular request without asking a service-wide counter to remember everybody's name.

## Verify the breakdown as well as the total

1. In staging, pick one counter and one reader. Use a small supported limit and generate more distinct attribute combinations than it can hold, using synthetic values only. Inspect the actual exported attributes; don't assume a particular combination will overflow.
2. Run the same workload on a fresh instance with the bounded attribute set. Check that the expected route and status groups are present, and reconcile their counts plus any overflow against the same input. Use fresh SDK state so a cumulative stream's old groups don't confuse the comparison.
3. Check the matching backend panel with and without its route filter. Add a separate signal for sustained overflow on important counters. For an additive counter, overflow increments divided by total increments over the same window measures the share of activity whose attributes were folded away. It doesn't identify which routes they belonged to.

Debugly's [Prometheus cardinality investigation](https://debugly.dev/prometheus-label-cardinality-memory/) covers a related cost: what can happen when the pipeline keeps too many series. Here the SDK is doing the opposite. It's limiting the detail it keeps, and the cost shows up in the questions the dashboard can no longer answer.

At the next instrumentation review, write down which questions each metric must answer, then give its dimensions a budget. A test that checks only the total can pass even when the one breakdown you need during an incident has already gone into the bin.