Skip to content

Conversation

@sl0thentr0py
Copy link
Member

Description

We are already parsing both sentry-trace and baggage headers and filling in sample_rand while constructing the propagation_context. We don't need the redundant logic in Transaction.continue_from_headers anymore.

@sl0thentr0py sl0thentr0py force-pushed the neel/unify-continue-trace branch from 9d6e74d to f8073f3 Compare November 26, 2025 22:34

optional_kwargs = {"source": source} if source else {}

return Transaction(
Copy link
Member Author

@sl0thentr0py sl0thentr0py Nov 26, 2025

Choose a reason for hiding this comment

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

annoyingly, the type signature of continue_trace that we decided to expose is inconsistent with the actual Transaction constructor, mainly that name and source should not be Optional in continue_trace but have default values instead.

We are already parsing both `sentry-trace` and `baggage` headers and
filling in `sample_rand` while constructing the `propagation_context`.
We don't need the redundant logic in `Transaction.continue_from_headers`
anymore.
@sl0thentr0py sl0thentr0py force-pushed the neel/unify-continue-trace branch from f8073f3 to 2b06f4f Compare November 26, 2025 22:36
@sl0thentr0py sl0thentr0py marked this pull request as ready for review November 26, 2025 22:44
@sl0thentr0py sl0thentr0py requested a review from a team as a code owner November 26, 2025 22:44
Comment on lines +1197 to 1207

return Transaction(
op=op,
origin=origin,
name=name,
source=source,
baggage=propagation_context.baggage,
parent_sampled=propagation_context.parent_sampled,
trace_id=propagation_context.trace_id,
parent_span_id=propagation_context.parent_span_id,
same_process_as_parent=False,
**optional_kwargs,
)
Copy link

Choose a reason for hiding this comment

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

Bug: The new continue_trace logic incorrectly populates baggage as a trace originator when an upstream sentry-trace exists without a baggage header.
Severity: CRITICAL | Confidence: High

🔍 Detailed Analysis

When an incoming sentry-trace header is present but no baggage header, the new PropagationContext.from_incoming_data() method leaves propagation_context.baggage as None. Consequently, Transaction.get_baggage() then populates a new mutable baggage from the transaction, causing the downstream service to incorrectly act as the trace originator. This deviates from the intended behavior of propagating the upstream trace context and can disrupt dynamic sampling and trace consistency across distributed systems.

💡 Suggested Fix

If sentry-trace is present but baggage is not, initialize propagation_context.baggage as an empty, frozen Baggage object to prevent Transaction.get_baggage() from populating it as a new trace originator.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: sentry_sdk/scope.py#L1187-L1207

Potential issue: When an incoming `sentry-trace` header is present but no `baggage`
header, the new `PropagationContext.from_incoming_data()` method leaves
`propagation_context.baggage` as `None`. Consequently, `Transaction.get_baggage()` then
populates a new mutable `baggage` from the transaction, causing the downstream service
to incorrectly act as the trace originator. This deviates from the intended behavior of
propagating the upstream trace context and can disrupt dynamic sampling and trace
consistency across distributed systems.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 3911558

Copy link
Member Author

@sl0thentr0py sl0thentr0py Nov 26, 2025

Choose a reason for hiding this comment

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

This is because we froze the DSC earlier in the transaction but not in the propagation context, I will fix this separately tomorrow. Also in some SDKs, we have given up freezing completely so maybe I'll just leave it out.

@codecov
Copy link

codecov bot commented Nov 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.97%. Comparing base (972c2d7) to head (2b06f4f).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5158      +/-   ##
==========================================
- Coverage   84.01%   83.97%   -0.05%     
==========================================
  Files         180      180              
  Lines       18303    18307       +4     
  Branches     3249     3251       +2     
==========================================
- Hits        15377    15373       -4     
- Misses       1927     1932       +5     
- Partials      999     1002       +3     
Files with missing lines Coverage Δ
sentry_sdk/scope.py 88.40% <100.00%> (-0.08%) ⬇️

... and 2 files with indirect coverage changes

trace_id=propagation_context.trace_id,
parent_span_id=propagation_context.parent_span_id,
same_process_as_parent=False,
**optional_kwargs,
Copy link

Choose a reason for hiding this comment

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

Bug: Missing baggage freeze loses third-party baggage items

When continuing a trace, the old code explicitly called baggage.freeze() in Transaction.continue_from_headers if there was a sentry-trace header. The new code passes propagation_context.baggage directly to the Transaction, but this baggage may remain mutable when the incoming baggage header contains only third-party items (no sentry items). Later, when Transaction.get_baggage() is called, it checks self._baggage.mutable and replaces the baggage with one from populate_from_transaction(), which does not preserve third_party_items. This causes third-party baggage items to be lost when propagating traces from upstream services that don't send sentry baggage items.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

we actually only call serialize with include_third_party=True in the celery case but not generally, so I will postpone this freezing / third party business for later after clarifying the requirements. As a refactor, this PR mostly does what it did before so it's fine.


transaction = Transaction.continue_from_headers(
normalize_incoming_data(environ_or_headers),
_sample_rand=sample_rand,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we already remove the _sample_rand arg and associated logic from continue_from_headers altogether or is that still used anywhere?

Copy link
Member Author

@sl0thentr0py sl0thentr0py Nov 27, 2025

Choose a reason for hiding this comment

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

will remove, technically breaking but it's an underscore so fine
nvm, let's do all this in the major, we will remove continue_from_headers itself.

@sl0thentr0py sl0thentr0py merged commit c3d389e into master Nov 27, 2025
155 checks passed
@sl0thentr0py sl0thentr0py deleted the neel/unify-continue-trace branch November 27, 2025 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants