feat(traces): OTLP span encoding and client-side validity - #950
Open
turnipdabeets wants to merge 3 commits into
Open
turnipdabeets wants to merge 3 commits into
turnipdabeets wants to merge 3 commits into
Conversation
Contributor
posthog-python Compliance ReportDate: 2026-09-15 14:29:55 UTC ✅ All Tests Passed!111/111 tests passed Capture_V1 Tests✅ 94/94 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
Contributor
Prompt To Fix All With AI### Issue 1
posthog/tracing/_sanitize.py:81
**Float timestamps can overflow**
A finite float such as `1e308` becomes infinite when multiplied by `1e9`, so `round()` or `int()` raises instead of returning `None`. This crashes span creation rather than falling back to the derived time. Validate the scaled value before converting it.
```suggestion
scaled = value * 1e9
if not math.isfinite(scaled):
return None
ns = int(round(scaled))
```
### Issue 2
posthog/tracing/_otlp.py:192
**Mapping traversal is unbounded**
`list(attributes.keys())` traverses and retains every key before the item and node limits are checked. A large mapping can consume excessive memory, while a custom non-terminating key iterator can hang encoding. Iterate over keys incrementally so encoding stops when its budget is exhausted.
```suggestion
for key in attributes.keys():
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(traces): OTLP span encoding and cli..." | Re-trigger Greptile |
This was referenced Sep 14, 2026
turnipdabeets
added this pull request to stack #958
September 14, 2026 23:03
turnipdabeets
force-pushed
the
traces/02-otlp-encoding
branch
from
September 15, 2026 14:09
599993b to
3a26165
Compare
Adds the OTLP JSON encoder for spans: AnyValue encoding per the traces spec (int64 as strings, out-of-range ints and non-finite floats as strings, None values and empty keys dropped, unpaired surrogates replaced with U+FFFD, a bounded walk that terminates on cycles), the span record builder with the W3C flags byte and OTel remoteness bits, the one-resource/one-scope envelope with service.name always present, and the name and timestamp sanitizers. One bad value otherwise gets the whole batch rejected. Not reachable from the client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TkZAsCciW4PV8ZdcCHmAbA
A finite float like 1e308 becomes infinite once scaled to nanoseconds, so round() raised instead of the value falling back to the derived time.
turnipdabeets
force-pushed
the
traces/02-otlp-encoding
branch
from
September 15, 2026 14:17
3a26165 to
efccf67
Compare
turnipdabeets
marked this pull request as ready for review
September 15, 2026 14:20
Contributor
Prompt To Fix All With AI### Issue 1
posthog/test/tracing/test_otlp.py:373-478
**Large inline expected payload**
This test embeds the complete nested OTLP payload as a large inline expected value. That violates the repository directive to keep extensive test results in external snapshot files. Move the payload to an external snapshot; this requirement must be satisfied before merging.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (2): Last reviewed commit: "fix(traces): reject a float time whose n..." | Re-trigger Greptile |
Keeps the ingestion-shape fixture in posthog/test/snapshots alongside the other server payload snapshots instead of a 100-line inline dict.
dustinbyrne
approved these changes
Sep 15, 2026
dustinbyrne
left a comment
Contributor
There was a problem hiding this comment.
Reviewed 284701863a1bca407428638086ea5ff23568df53 via source inspection and existing CI. No issues found in this staged OTLP encoding/validity slice. Approval covers this slice; #949 precedes it in the stack.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
Adds the OTLP JSON encoder for spans. AnyValue encoding follows the traces spec: int64 as strings, out-of-range ints and non-finite floats as strings,
Nonevalues and empty keys dropped, unpaired surrogates replaced with U+FFFD, and a bounded walk that terminates on cycles. Also adds the span record builder (W3C flags byte and OTel remoteness bits), the one-resource/one-scope envelope withservice.namealways present, and the name and timestamp sanitizers.Why it matters: one bad value otherwise gets the whole batch rejected by ingestion. Not reachable from the client yet.
Stack (PR 2 of 9, based on
traces/01-ids-traceparent):traces/01-ids-traceparenttraces/02-otlp-encoding← this PRtraces/03-span-handlestraces/04-transporttraces/05-pipelinetraces/06-exporttraces/07-span-limitstraces/08-before-span-sendtraces/09-client-wiring💚 How did you test it?
Unit tests in
posthog/test/tracing/test_otlp.pyandtest_sanitize.pycover each encoding rule, the envelope shape and the sanitizers.📝 Checklist
If releasing new changes
sampo addto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Implemented with Claude Code (Claude Opus 5) against the traces spec, one commit per slice so each PR reviews on its own. Rebased onto main and opened as a stacked draft in a later Claude Code session (Claude Fable 5.1).
🤖 Generated with Claude Code
https://claude.ai/code/session_012o7CtHLfcypjmXL7g9ZGRC