You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
So 429 is treated as permanent and the batch is dropped. The OTLP spec (specification/protocol/exporter.md) requires retrying on 429; the gRPC sibling already honors throttling via the google.rpc.retryinfo-bin trailer.
2. The Retry-After response header is never read
The retry loops in trace_exporter/__init__.py, _log_exporter/__init__.py, and metric_exporter/__init__.py compute the delay solely from exponential backoff and never inspect Retry-After, which servers send with 429/503. The spec requires honoring it; Go and Java both parse it (delay-seconds and HTTP-date forms).
3. The exponential backoff is uncapped
Each exporter computes backoff = 2**retry_num * random.uniform(0.8, 1.2) with _MAX_RETRYS = 6, reaching ~32s with no clamp; the gRPC exporter has the same uncapped pattern and applies the server retry_delay with no ceiling. Go and Java cap the interval.
Proposed fix
Add 429 to the retryable set in _is_retryable.
Add a Retry-After parser (delay-seconds integer and HTTP-date) and use its value as the next-retry delay across the trace/metric/log HTTP exporters.
Add a MAX_BACKOFF clamp (min(backoff, MAX_BACKOFF)) to the HTTP exporters and the gRPC exporter.
Activity
ocelotl commentedon Jul 22, 2026
🔒 Internal (dash0) — not for upstream.
Tracking for the OTLP HTTP exporter retry fix.
oss_13_http_exporter_retry_after_backoff_capc29c774bc· PR: oss_13_http_exporter_retry_after_backoff_cap #27ocelotl commentedon Jul 22, 2026
📣 Public-facing draft — to be opened upstream in
open-telemetry/opentelemetry-python. No internal references; copy verbatim.Title: OTLP HTTP exporter ignores
Retry-After, does not retry on 429, and has no backoff capDescription
The OTLP HTTP exporters (trace, metric, logs) do not conform to the OTLP exporter spec's retry requirements in three ways.
1. HTTP
429 Too Many Requestsis not retriedexporter/opentelemetry-exporter-otlp-proto-http/.../_common/__init__.py,_is_retryable()only retries408and5xx:So
429is treated as permanent and the batch is dropped. The OTLP spec (specification/protocol/exporter.md) requires retrying on429; the gRPC sibling already honors throttling via thegoogle.rpc.retryinfo-bintrailer.2. The
Retry-Afterresponse header is never readThe retry loops in
trace_exporter/__init__.py,_log_exporter/__init__.py, andmetric_exporter/__init__.pycompute the delay solely from exponential backoff and never inspectRetry-After, which servers send with429/503. The spec requires honoring it; Go and Java both parse it (delay-seconds and HTTP-date forms).3. The exponential backoff is uncapped
Each exporter computes
backoff = 2**retry_num * random.uniform(0.8, 1.2)with_MAX_RETRYS = 6, reaching ~32s with no clamp; the gRPC exporter has the same uncapped pattern and applies the serverretry_delaywith no ceiling. Go and Java cap the interval.Proposed fix
429to the retryable set in_is_retryable.Retry-Afterparser (delay-seconds integer and HTTP-date) and use its value as the next-retry delay across the trace/metric/log HTTP exporters.MAX_BACKOFFclamp (min(backoff, MAX_BACKOFF)) to the HTTP exporters and the gRPC exporter.ocelotl commentedon Sep 9, 2026
Fixed by open-telemetry#5252 open-telemetry#5389