dbt-labs/dbt-core · error
attrs_updater should only be called once
Error message
attrs_updater should only be called once
What it means
Panic from `.expect()` after `attrs_updater.take()` in `find_and_record_span_status_from_attrs`. The updater is passed as `Option<impl FnOnce>` and must be consumed exactly once; a `None` here means the function's internal calling convention was violated (updater already taken or never supplied).
Source
Thrown at crates/dbt-tracing/src/span_info.rs:431
{
let mut attrs_updater = Some(attrs_updater);
with_current_span(move |span_ref| {
// Find the closest span with the expected TelemetryAttributes type.
// Scope iterator starts from the current span and goes up to the root.
for span_ref in span_ref.scope() {
let mut span_ext_mut = span_ref.extensions_mut();
if let Some(attrs) = span_ext_mut
// Get the current attributes
.get_mut::<TelemetryAttributes>()
.expect("Telemetry hasn't been properly initialized. Missing span event attributes")
// Try downcasting to the expected type
.downcast_mut::<A>()
{
// Found the expected attributes, call the updater and return
attrs_updater
.take()
.expect("attrs_updater should only be called once")(attrs);
// Record the status of the span from the attrs themselves
if let Some(status) = attrs.get_span_status() {
span_ext_mut.replace(status);
}
return;
}
}
});
}
/// Reads span start info from the given span with read-only access.
///
/// This provides immutable access to the span's start information including
/// trace_id, span_id, span_name, and other metadata.
///
/// Returns `None` if span is disabled.View on GitHub (pinned to 0267ce9170)
Solutions
- Ensure every call to `find_and_record_span_status_from_attrs` supplies a fresh `Some(updater)` closure.
- Audit the calling path (`record_test_span_with_detail`, `execute_unit_test_remote`, `process_unit_test_result`) for double invocation of the same updater value.
- If hit after a refactor, restructure so the updater is moved into the function rather than shared.
Example fix
// before
find_and_record_span_status_from_attrs(span, None);
// after
find_and_record_span_status_from_attrs(span, Some(Box::new(|attrs| { attrs.set_status(...); }))); Defensive patterns
Strategy: validation
Validate before calling
// assert updater supplied exactly once before calling assert!(attrs_updater.is_some(), "updater must be Some"); find_and_record_span_status_from_attrs(span, attrs_updater);
Prevention
- Treat the updater as `impl FnOnce` moved into the call so the compiler enforces single use.
- Never reuse an `Option< updater >` across two invocations.
- Keep the function private if the exactly-once contract is internal.
When it happens
Trigger: Calling `find_and_record_span_status_from_attrs` with a `None` updater, or a code path that invokes the function twice reusing the same consumed updater Option — only reachable through an internal caller bug since the public wrappers always construct the updater.
Common situations: Custom forks or patched call sites that pass `None` or double-call the function; refactors that moved the `take()` call without preserving the exactly-once contract.
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
- Parent span must have a SpanStartInfo record in its extensio
- Telemetry hasn't been properly initialized. Missing span sta
- Parquet writer is not initialized
- expected_field should be Struct
- Failed to serialize some records: {errors}
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/ef985b85b9489b96.
Report an issue: GitHub.