stalwartlabs/stalwart · error

Missing span ID

Error message

Missing span ID

What it means

When the collector processes a connection-end event (IMAP/POP3/SMTP/ManageSieve connection end or attempt end) it removes the corresponding active span keyed by the event's span ID. The event carries no span ID (span_id() returned None), so the expect panics. This indicates an event that should always carry a span ID arrived without one — e.g. an end event emitted without a matching start.

Source

Thrown at crates/trc/src/ipc/collector.rs:164

                                    if self.active_spans.len() > STALE_SPAN_CHECK_WATERMARK {
                                        self.active_spans.retain(|_, span| {
                                            timestamp.saturating_sub(span.inner.timestamp)
                                                < SPAN_MAX_HOLD
                                        });
                                    }
                                    event
                                }

                                HTTP_CONN_END
                                | IMAP_CONN_END
                                | POP3_CONN_END
                                | SMTP_CONN_END
                                | MANAGE_SIEVE_CONN_END
                                | EV_ATTEMPT_END => {
                                    if let Some(span) = self
                                        .active_spans
                                        .remove(&event.span_id().expect("Missing span ID"))
                                    {
                                        event.inner.span = Some(span.clone());
                                    } else {
                                        #[cfg(any(feature = "dev_mode", feature = "test_mode"))]
                                        {
                                            if event.span_id().unwrap() != 0 {
                                                eprintln!("Unregistered span ID: {event:?}");
                                            }
                                        }
                                    }
                                    Arc::new(event)
                                }
                                _ => {
                                    if let Some(span_id) = event.span_id() {
                                        if let Some(span) = self.active_spans.get(&span_id) {
                                            event.inner.span = Some(span.clone());
                                        } else {
                                            #[cfg(any(

View on GitHub (pinned to e962003857)

Solutions

  1. Upgrade all nodes writing events to the same version so span IDs are always emitted with end events
  2. Make the code tolerate missing span IDs (use match/if-let with a default of 0 instead of expect)
  3. Clear stale event queues/spans after a restart to avoid orphan end events
  4. Audit instrumentation so every *_CONN_END emission is paired with a started span

Example fix

// before
if let Some(span) = self.active_spans.remove(&event.span_id().expect("Missing span ID")) {
// after
if let Some(span_id) = event.span_id() {
    if let Some(span) = self.active_spans.remove(&span_id) {
Defensive patterns

Strategy: type-guard

Validate before calling

if event.span_id().is_none() { log::warn!("end event without span id, skipping"); return; }

Type guard

fn has_span_id(e: &Event) -> bool { e.span_id().is_some() }

Try / catch

if let Some(id) = event.span_id() {
    if let Some(span) = self.active_spans.remove(&id) { event.inner.span = Some(span.clone()); }
}

Prevention

When it happens

Trigger: Receiving *_CONN_END or EV_ATTEMPT_END telemetry events whose span_id() is None, i.e. events emitted outside an instrumented span or produced by a mismatched event instrumentation.

Common situations: A connection was closed after server restart (start event lost), mixed server versions writing incompatible event formats to the shared event stream, or a code path emitting end events without entering the span.

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/f674f1b4ca195056. Report an issue: GitHub.