Skip to content

fix(otlp): cap tracestate at W3C 32-member limit after sampling writeback - #311

Open
detail-app[bot] wants to merge 1 commit into
masterfrom
detail/bug-fix/fix-otlp-cap-tracestate-at-w3c-32-member-limit-aft-ee7b15
Open

detail-app[bot] wants to merge 1 commit into
masterfrom
detail/bug-fix/fix-otlp-cap-tracestate-at-w3c-32-member-limit-aft-ee7b15

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 16, 2026

Copy link
Copy Markdown

Detail bug report: View on Detail

Fixes ENG-712

Bug

mergeOtTracestate in src/signals/otlp/traces.zig rebuilds a span's trace_state after a probabilistic keep trace-sampler writes its threshold back as ot=th:VALUE. It always emits a leading ot= list-member and then appends every existing non-ot= vendor entry with no count check, so a W3C-compliant 32-member incoming trace_state (no ot= entry) became a 33-member output — violating the W3C Trace Context normative cap of 32 list-members (§3.3.1.1 / §3.3.1.2 ABNF list = list-member 0*31( ... )). That result is assigned straight to span.trace_state and re-serialized verbatim by the OTLP JSON and protobuf encoders, so a spec-non-conforming value went on the wire.

The cap was lost in commit b7b6419b: that refactor replaced the vendored updateTracestateInPlace helper (which enforces the 32-member cap) with a hand-written merge to preserve non-th: ot= sub-keys (rv:, p:), but omitted the count cap and it was never re-added.

Fix

Augmented mergeOtTracestate's other-vendor loop with the 32-member cap: entry_count starts at 1 (the always-emitted ot= member) and if (entry_count >= 32) break; runs before each vendor append, dropping the least-recent (rightmost) vendor once the cap is reached.

This preserves Edge's existing non-th: ot= sub-key carry-through — it does not swap in the vendored helper, which wholesale-drops ot= and would strip rv:/p: data and regress the existing tests. Non-th: ot= sub-keys live inside the single ot= member and so don't count against the member limit.

Testing

  • Added 4 tests in src/signals/otlp/traces.zig:
    • mergeOtTracestate - 32 vendors capped at W3C 32-member limit: the exact reported case — a compliant 32-member vendor-only input stays at 32 members (not 33) after the ot=th: prepend.
    • mergeOtTracestate - 31 vendors stays within cap, all preserved: boundary guard against over-truncation — at 31 vendors all are preserved (output = ot= + 31 = 32).
    • mergeOtTracestate - ot sub-keys preserved when vendor cap reached: guards against the footgun of adopting the vendored helper — ot=rv:abc,... keeps rv: (as ot=rv:abc;th:VALUE) while still clamping to 32 members.
    • processTraces - 100%-keep writeback keeps traceState within W3C 32-member limit: end-to-end through the public processTracesStream JSON path (decode → filter → sample → writeback → re-encode) with a 100%-keep policy, asserting the serialized traceState has exactly 32 members, is prefixed ot=th:0,, and v31=x is absent.
  • Mutation check: temporarily neutralizing the cap makes exactly the cap-enforcement tests fail (and the boundary tests pass by design), confirming the tests exercise the fix rather than tautologies.
  • zig build test --summary all (Debug and ReleaseSafe), zig fmt --check, ziglint, and zig build / zig build -Doptimize=ReleaseSafe all pass. Suite is green (523 pass / 1 skip; +4 net tests vs. baseline), no leaks.

Automatic Fixes PRs can be configured here.

@macroscopeapp

macroscopeapp Bot commented Sep 16, 2026

Copy link
Copy Markdown

Approvability

Verdict: Approved at 924e977

Macroscope's review found this PR approvable — This is a narrowly scoped OTLP bug fix that prevents sampling writeback from producing invalid 33-member W3C tracestate values while preserving existing ot= subkeys and normal under-limit behavior. The accompanying tests cover the boundary and public JSON path.

You can add or adjust custom eligibility rules. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant