Skip to content

W3C Baggage propagator uses form encoding instead of percent encoding - #5567

Open
dwin-gharibi wants to merge 2 commits into
open-telemetry:mainfrom
dwin-gharibi:fix/baggage-percent-encoding
Open

W3C Baggage propagator uses form encoding instead of percent encoding#5567
dwin-gharibi wants to merge 2 commits into
open-telemetry:mainfrom
dwin-gharibi:fix/baggage-percent-encoding

Conversation

@dwin-gharibi

@dwin-gharibi dwin-gharibi commented Aug 23, 2026

Copy link
Copy Markdown

Closes #5566.

Description

W3CBaggagePropagator encodes and decodes baggage with quote_plus / unquote_plus, which is application/x-www-form-urlencoded. That form maps a space to + and decodes + back to a space. The W3C Baggage grammar defines baggage-octet as %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E, so + (%x2B) is an ordinary literal that must be preserved, and SP (%x20) is excluded and must be percent-encoded as %20.

Both directions are wrong, and neither raises: values are silently corrupted as they cross a service boundary.

Root cause

opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py imports quote_plus / unquote_plus and uses them at lines 118-119 (extract) and 167 (_encode_baggage_pairs). The +-for-space substitution is specific to HTML form submission and is not part of RFC 3986 percent-encoding, which is what the Baggage specification requires.

Approach

Replace quote_plus / unquote_plus with quote / unquote. quote is called with safe="" so that every character outside the unreserved set is escaped; over-encoding is lossless and keeps the output unambiguous for any compliant parser.

Files changed

  • opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py
  • opentelemetry-api/tests/propagators/test_w3cbaggagepropagator.py
  • .changelog/5561.fixed

Testing

Eight new tests cover the contract in both directions: literal + preserved on extract (including the +-only and repeated-+ cases that previously lost data to strip()), + in keys, %20 decoded to a space, %20 emitted for a space, and inject/extract round trips.

test_encode_baggage_pairs previously asserted against a local re-implementation of the encoder rather than the module's own _encode_baggage_pairs, so it could never have caught this. It now calls the real helper.

Two existing assertions encoded the buggy output (transaction=string+with+spaces and key=val+ue) and were updated to %20. Note that the extract direction - the one that corrupts inbound data - had no test at all.

Result: 281 passed, 1 skipped across opentelemetry-api (273 baseline plus 8 new).

Risk / compatibility

The wire format changes for values containing spaces or other characters outside the unreserved set. This is the intended correction and brings Python in line with the other OpenTelemetry SDKs, but it is observable: anyone asserting on raw baggage headers in their own tests will see %20 where they previously saw +. Decoding remains backward compatible for every correctly-encoded header.

The W3C Baggage grammar defines baggage-octet as
%x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E, so "+" (%x2B) is an ordinary
literal that must survive a round trip, while SP (%x20) is excluded and has
to be percent-encoded.

Add coverage for both directions and point test_encode_baggage_pairs at the
real _encode_baggage_pairs helper instead of a local re-implementation that
could never fail. Update the two assertions that encoded the form-encoded
behaviour.

These tests fail against the current implementation.
W3CBaggagePropagator used quote_plus/unquote_plus, which is
application/x-www-form-urlencoded: it emits "+" for a space and decodes "+"
back to a space. The W3C Baggage grammar lists "+" (%x2B) as an ordinary
baggage-octet that must be preserved verbatim, and excludes SP (%x20), which
must be percent-encoded.

Both directions were affected. On extract, a literal "+" from a compliant
peer was silently turned into a space, and a value of "+" decoded to an empty
string once the trailing strip() ran. On inject, a value containing a space
was emitted as "+", which a compliant peer reads back as a literal "+".

Switch to quote/unquote so the wire format matches the specification and
values survive a round trip across implementations.
Copilot AI lite review requested due to automatic review settings August 23, 2026 16:10
@dwin-gharibi
dwin-gharibi requested a review from a team as a code owner August 23, 2026 16:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 23, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-08-23 16:16 UTC

Review the latest changes.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

W3C Baggage propagator uses form encoding instead of percent encoding

2 participants