Repository navigation
Error events carry no trace context, so an issue is never linked to the trace it happened in #1174
Description
Summary
Error events are the only event type the SDK sends without trace context. Sentry.Event.create_event/1 sets contexts: to generate_contexts(), which returns %{os: ..., runtime: ...} and nothing else, and no later stage adds a :trace key. So an exception captured inside an active span arrives at Sentry with no link to the trace it happened in, even when the SDK itself exported that trace over the same transport moments earlier.
The visible symptom is that the issue's trace_id equals its event_id — Relay fabricates a trace id from the event id when the payload carries none. In the UI the issue's "Trace" tab shows only itself, and none of the spans, logs or other errors from the same request appear.
Why this looks like an oversight rather than a decision
Three of the four event types the SDK sends already carry trace context, and the fourth documents it as the way to reach errors:
- Transactions carry it:
Sentry.Transaction's typespec makescontexts.tracerequired, andSentry.OpenTelemetry.SpanProcessor.build_trace_context/1fills it in. - Logs carry it:
Sentry.LogEvent.extract_trace_context/1readstrace_id/span_idout of logger metadata (as written there byopentelemetry_logger_metadata), and falls back toSentry.UUID.uuid4_hex()when absent. - Check-ins carry it, and the option's own docs say why: "right now Sentry supports the
trace_idkey under the trace context to connect the check-in with related errors" (Sentry.CheckIn, the:contextsschema entry). - Errors carry nothing.
So the SDK will happily connect a cron check-in to related errors, but the errors themselves cannot be connected to anything. The docs for check-ins describe a linkage that the error side of does not exist.
There is also no way for a user to supply it: Sentry.Context.context_keys/0 is [:breadcrumbs, :tags, :user, :extra, :request, :attachments], with no trace slot, so Sentry.Context cannot be used to work around this either.
Reproduction
With tracing configured (Sentry.OpenTelemetry.SpanProcessor + Sentry.OpenTelemetry.Sampler, traces_sample_rate: 1.0):
require OpenTelemetry.Tracer, as: Tracer
Tracer.with_span "repro" do
# The SDK knows the trace here — it is about to export it as a transaction.
IO.inspect(:otel_span.hex_trace_id(Tracer.current_span_ctx()), label: "active trace")
event = Sentry.Event.create_event(message: "hello")
IO.inspect(event.contexts, label: "contexts")
# => contexts: %{os: %{...}, runtime: %{...}} <- no :trace
Sentry.capture_message("hello")
endIn Sentry, the resulting issue's trace_id is its own event_id, not the trace id printed above.
Expected
An event captured while a valid (non-zero, non-noop) span context is in scope should go out with contexts.trace set from that span — at minimum trace_id and span_id, formatted as the transaction path already formats them, so the two agree. With no active span, behaviour should stay exactly as it is today.
This looks cheap to do centrally: :before_send already runs synchronously in the capturing process (Sentry.Client.send_event/2 → maybe_call_before_send/2 → function.(event), no task or spawn), and both the plug capture path and the crash-report handler run in the process that owns the span, so the span context is simply in scope at event-creation time.
Workaround
For anyone else hitting this, a :before_send callback covers it in about ten lines:
def call(%Sentry.Event{} = event) do
ctx = :otel_tracer.current_span_ctx()
if :otel_span.is_valid(ctx) do
trace = %{trace_id: :otel_span.hex_trace_id(ctx), span_id: :otel_span.hex_span_id(ctx)}
%{event | contexts: Map.put(event.contexts || %{}, :trace, trace)}
else
event
end
end
def call(other), do: otherTwo notes for anyone copying it: contexts is nil on a hand-built %Sentry.Event{} (the struct default — only create_event/1 fills it in), and the callback also receives transactions, so it has to pass through anything that is not an %Sentry.Event{} or it takes tracing down with it.
Versions
Verified against sentry 12.0.3 from Hex, reading lib/sentry/event.ex, lib/sentry/context.ex, lib/sentry/client.ex, lib/sentry/plug_capture.ex and lib/sentry/opentelemetry/. I also checked master: create_event/1 still assigns contexts: generate_contexts(), generate_contexts/0 still returns os and runtime only, and client.ex still attaches no trace on the send path. I have not run 13.4.2 directly, so if this was addressed somewhere I did not look, apologies for the noise.
I could not find an existing issue covering this — #1011 and #1163 both concern spans going missing from traces, which is a different failure.
Happy to open a PR if you would like it fixed the way described above, or a different way if you would rather it live somewhere other than create_event/1.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsWaiting for: Product Owner
Activity
linear-code commentedon Aug 17, 2026
ELIXIR-73