feat: Propagate OTel context via WebSocket HTTP upgrade headers#174
Merged
feat: Propagate OTel context via WebSocket HTTP upgrade headers#174
Conversation
Propagate traceparent, tracestate, and baggage through the WebSocket connection using standard W3C HTTP headers on the upgrade request, matching how any HTTP-based service would propagate OTel context. Client side (v1 + v2): - Use propagate.inject() to capture the current OTel context into a headers dict, then pass it as extra_headers/additional_headers to websockets.connect(). Server side: - In Server.serve(), use propagate.extract() on websocket.request_headers to restore the OTel context, then attach it as the ambient context for the lifetime of the connection.
darshkpatel
approved these changes
Mar 1, 2026
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.
Why
River's WebSocket connections don't carry any OTel context (traceparent, tracestate, baggage) from client to server. This means distributed tracing and baggage propagation are broken at the WebSocket boundary — the server has no way to inherit the caller's trace context or read OTel baggage entries.
What changed
Uses the standard W3C HTTP header approach — the same mechanism any HTTP service uses for OTel propagation — applied to the WebSocket upgrade request.
Client side (
client_transport.py,v2/session.py)websockets.connect(), inject the current OTel context into a headers dict viapropagate.inject().extra_headers(v1 legacy API) /additional_headers(v2 asyncio API) to the connect call.traceparent,tracestate, andbaggageheaders if the corresponding propagators are configured in the global textmap.Server side (
server.py)Server.serve(), extract the OTel context fromwebsocket.request_headersviapropagate.extract().context.attach()/context.detach().baggage.get_all()and inherits the caller's trace context.Tests (
tests/v1/test_opentelemetry.py)test_baggage_propagated_via_ws_headers: Sets two baggage entries on the client, verifies the server handler can read them.test_no_baggage_when_none_set: Verifies clean behavior when no baggage is set.test_traceparent_propagated_via_ws_headers: Sets both an active span and baggage on the client, verifies both propagate.Test plan
All existing tests pass unchanged. The 3 new tests verify end-to-end OTel context propagation through the WebSocket connection.
Revertibility
Safe to revert — only adds new
extra_headers/additional_headerstowebsockets.connect()and apropagate.extract()+context.attach()wrapper on the server. No wire protocol changes, no schema changes, no data mutations.~ written by Zerg 👾