vectordotdev/vector · error
span must already exist!
Error message
span must already exist!
What it means
In the tracing `Layer`'s `on_new_span`, the code looks up the span by id with `ctx.span(id)` and expects it to exist since the callback fires for a newly created span. If the registry returns `None`, the `expect("span must already exist!")` panics — a layer/registry invariant violation.
Solutions
- Use this layer with the standard `tracing_subscriber::Registry` it is designed for
- Verify layer ordering (registry-based layers must sit above the Registry)
- If using a custom subscriber, implement span lookup correctly or skip the extension insert
- File a bug with subscriber setup if the standard registry still triggers it
Example fix
// before
let span = ctx.span(id).expect("span must already exist!");
// after
if let Some(span) = ctx.span(id) {
span.extensions_mut().insert(fields);
} Defensive patterns
Strategy: type-guard
Type guard
// narrow instead of expect
fn span_fields<S>(ctx: &Context<'_, S>, id: &tracing_core::span::Id) -> Option<SpanFields> {
ctx.span(id).map(|span| {
let mut f = SpanFields::default();
span.extensions_mut().insert(f.clone());
f
})
} Prevention
- Always pair the layer with tracing_subscriber::Registry
- Check layer ordering relative to the Registry
- Avoid custom subscribers unless span lookup is implemented
- Replace expect with graceful skip when spans may be absent
When it happens
Trigger: A custom `Subscriber`/`Registry` combination where `Context::span(id)` returns `None` for a span id passed to `on_new_span`, e.g. a non-registry subscriber or spans being dropped concurrently with creation.
Common situations: Using this layer with a subscriber other than the tracing Registry it assumes; custom layering where span lifetimes are managed unusually; forks altering registry behavior.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Couldn't acquire lock on internal logs buffer
- early buffer was already consumed
- poisoned locks are dumb
- a record with a next ID must have an event count
- a valid HTTP/1 URI is valid as an HTTP URI
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/747de2061f83cafd.
Report an issue: GitHub.
Appendix: source
Thrown at src/trace.rs:356
}
}
}
}
// Try buffering the event, and if we're not buffering anymore, try to
// send it along via the trace sender if it's been established.
if !try_buffer_event(&log) {
try_broadcast_event(log);
}
}
}
fn on_new_span(
&self,
attrs: &tracing_core::span::Attributes<'_>,
id: &tracing_core::span::Id,
ctx: Context<'_, S>,
) {
let span = ctx.span(id).expect("span must already exist!");
let mut fields = SpanFields::default();
attrs.values().record(&mut fields);
span.extensions_mut().insert(fields);
}
}
#[derive(Default, Debug)]
struct SpanFields(HashMap<&'static str, Value>);
inventory::submit!(SpanField("component_id"));
inventory::submit!(SpanField("component_type"));
inventory::submit!(SpanField("component_kind"));
/// Snapshot of every registered [`SpanField`],
/// materialized once on first access. `inventory` populates submissions before `main`, so the
/// snapshot is guaranteed to capture every entry; the read path on every traced span event is
/// then a single set lookup against this static.
static SPAN_FIELDS: LazyLock<HashSet<&'static str>> =View on GitHub (pinned to bdb87aeaa4)