Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- Enable compression for forwarded uploads. ([#5965](https://github.com/getsentry/relay/pull/5965))
- Change the default partitioning for the envelope buffer from semantic to round-robin. ([#5967](https://github.com/getsentry/relay/pull/5967))
- Enable retries for upload requests to upstream. ([#5975](https://github.com/getsentry/relay/pull/5975))
- Unconditionally create a trace context with a trace id for errors. ([#5979](https://github.com/getsentry/relay/pull/5979))

## 26.4.2

Expand Down
1 change: 0 additions & 1 deletion relay-cabi/src/processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@ pub unsafe extern "C" fn relay_store_normalizer_normalize_event(
span_allowed_hosts: &[], // only supported in relay
span_op_defaults: Default::default(), // only supported in relay
performance_issues_spans: Default::default(),
derive_trace_id: Default::default(),
};
normalize_event(&mut event, &normalization_config);

Expand Down
3 changes: 0 additions & 3 deletions relay-dynamic-config/src/feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,6 @@ pub enum Feature {
/// Upload non-prosperodmp playstation attachments via the upload endpoint.
#[serde(rename = "projects:relay-playstation-uploads")]
PlaystationUploads,
/// Add a random trace ID to events that lack one.
#[serde(rename = "organizations:relay-default-trace-id")]
AddDefaultTraceID,
/// Enable experimental expansion of the unreal report in the endpoint rather than in the
/// processor. Only enable for organizations with sufficient attachment quota.
#[serde(rename = "organizations:relay-unreal-endpoint-expansion")]
Expand Down
28 changes: 11 additions & 17 deletions relay-event-normalization/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use relay_event_schema::protocol::{
AsPair, Attributes, AutoInferSetting, ClientSdkInfo, Context, ContextInner, Contexts,
DebugImage, DeviceClass, Event, EventId, EventType, Exception, Headers, IpAddr, Level,
LogEntry, Measurement, Measurements, PerformanceScoreContext, ReplayContext, Request, Span,
SpanStatus, SpanV2, Tags, Timestamp, TraceContext, User, VALID_PLATFORMS,
SpanStatus, SpanV2, Tags, Timestamp, TraceContext, TraceId, User, VALID_PLATFORMS,
};
use relay_protocol::{
Annotated, Empty, Error, ErrorKind, FiniteF64, FromValue, Getter, Meta, Object, Remark,
Expand Down Expand Up @@ -168,9 +168,6 @@ pub struct NormalizationConfig<'a> {

/// Set a flag to enable performance issue detection on spans.
pub performance_issues_spans: bool,

/// Should add a random trace ID to events that lack one.
pub derive_trace_id: bool,
}

impl Default for NormalizationConfig<'_> {
Expand Down Expand Up @@ -205,7 +202,6 @@ impl Default for NormalizationConfig<'_> {
span_allowed_hosts: Default::default(),
span_op_defaults: Default::default(),
performance_issues_spans: Default::default(),
derive_trace_id: Default::default(),
}
}
}
Expand Down Expand Up @@ -334,7 +330,7 @@ fn normalize(event: &mut Event, meta: &mut Meta, config: &NormalizationConfig) {
let event_id = event.id.value().unwrap().0;

// Some contexts need to be normalized before metrics extraction takes place.
normalize_contexts(&mut event.contexts, event_id, config);
normalize_contexts(&mut event.contexts, event_id);

if config.normalize_spans && event.ty.value() == Some(&EventType::Transaction) {
crate::normalize::normalize_app_start_spans(event);
Expand Down Expand Up @@ -1355,15 +1351,9 @@ fn remove_logger_word(tokens: &mut Vec<&str>) {
}

/// Normalizes incoming contexts for the downstream metric extraction.
fn normalize_contexts(
contexts: &mut Annotated<Contexts>,
event_id: Uuid,
config: &NormalizationConfig,
) {
if config.derive_trace_id {
// We will always need a TraceContext.
let _ = contexts.get_or_insert_with(Contexts::new);
}
fn normalize_contexts(contexts: &mut Annotated<Contexts>, event_id: Uuid) {
// We will always need a TraceContext.
Comment thread
cursor[bot] marked this conversation as resolved.
let _ = contexts.get_or_insert_with(Contexts::new);

let _ = processor::apply(contexts, |contexts, _meta| {
// Reprocessing context sent from SDKs must not be accepted, it is a Sentry-internal
Expand All @@ -1374,8 +1364,13 @@ fn normalize_contexts(
// We need a TraceId to ingest the event into EAP.
// If the event lacks a TraceContext, add a random one.

if config.derive_trace_id && !contexts.contains::<TraceContext>() {
if !contexts.contains::<TraceContext>() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can unify the branches with let trace_ctx = contexts.get_or_default::<TraceContext>()

contexts.add(TraceContext::random(event_id))
Copy link
Copy Markdown
Member

@shashjar shashjar May 12, 2026

Choose a reason for hiding this comment

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

If there's a TraceContext with valid information but it's missing trace_id (not sure if this actually occurs in practice) - I think we'll end up overriding the entire context with a new one that has only trace_id? I think we want to get_or_default the trace context, then check whether trace_context.trace_id is null, and then set only trace_context.trace_id if so

} else {
let trace_ctx = contexts.get_or_default::<TraceContext>();
if trace_ctx.trace_id.0.is_none() {
trace_ctx.trace_id = Annotated::new(TraceId::random())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistent trace ID derivation between if/else branches

Medium Severity

When no TraceContext exists, TraceContext::random(event_id) derives the trace ID deterministically from the event_id (using TraceId::from(event_id)) and attaches a trace_id.missing remark. But when a TraceContext exists with a missing trace_id, the else branch uses TraceId::random() which generates a truly random UUID via Uuid::new_v4() and attaches no remark. This means the trace ID strategy differs depending on whether an SDK sent an empty TraceContext or none at all, and the provenance metadata is lost in the else case.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9942b0b. Configure here.

}

for annotated in &mut contexts.0.values_mut() {
Expand Down Expand Up @@ -4958,7 +4953,6 @@ mod tests {
&mut Meta::default(),
&NormalizationConfig {
performance_score: Some(&performance_score),
derive_trace_id: true,
..Default::default()
},
);
Expand Down
1 change: 0 additions & 1 deletion relay-server/src/processing/utils/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ pub fn normalize(
performance_issues_spans: ctx
.project_info
.has_feature(Feature::PerformanceIssuesSpans),
derive_trace_id: project_info.has_feature(Feature::AddDefaultTraceID),
};

metric!(timer(RelayTimers::EventProcessingNormalization), {
Expand Down
Loading