dbt-labs/dbt-core · warning · FsError
Generic
Generic
Error message
Failed to detach invocation telemetry layers: {} What it means
During tracing teardown, the dbt runtime detaches per-invocation telemetry layers via the tracing reload handle. If reload_telemetry(vec![], vec![]) fails, the FsError is collected and reported with this message. It means invocation-scoped log/trace layers could not be removed — usually a symptom of a telemetry-subscriber internal problem, not of user input.
Solutions
- Check the interpolated {} detail in the message for the underlying reload failure
- Ensure dbt's tracing init/teardown runs exactly once per process invocation
- Look for other errors logged before this one during shutdown_items shutdown; fix the root cause there
- If seen in tests, isolate subscriber initialization per test or use a serial test harness
Example fix
// before let mut telemetry = TeardownGuard::new(); let mut telemetry2 = TeardownGuard::new(); // second init corrupts reload handle // after let mut telemetry = TeardownGuard::new(); // ... single teardown at process end telemetry.finish();
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure telemetry is initialized exactly once static INIT: Once = Once::new(); INIT.call_once(init_dbt_tracing);
Type guard
fn telemetry_active(handle: &ReloadHandle) -> bool { handle.is_initialized() } Try / catch
if let Err(e) = self.reload_handle.reload_telemetry(vec![], vec![]) {
log::warn!("telemetry detach failed (non-fatal): {e}");
} Prevention
- Initialize tracing once per process
- Check earlier shutdown errors as the likely root cause
- Avoid swapping the global subscriber externally while dbt runs
When it happens
Trigger: Calling finish() or dropping the teardown guard when the reload handle's subscriber no longer matches the registered layers (already torn down, reinitialized subscriber, or a poisoned reload registry).
Common situations: Double-initialization of the tracing subscriber in the same process (e.g. tests calling dbt init twice); a shutdown ordering bug where layers were already dropped; running under a custom subscriber that replaced dbt's.
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
- attrs_updater should only be called once
- failed to write compiled code to stdout
- Task should always operate on at least a single node
- Task that doesn't belong to a phase should override…
- Telemetry hasn't been properly initialized. Missing span…
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/3887cb08a3df2a30.
Report an issue: GitHub.
Appendix: source
Thrown at crates/dbt-common/src/tracing/dbt_init.rs:216
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn teardown(&mut self) -> Vec<FsError> {
// Set before the work, not after: a panicking shutdown would otherwise be retried
// during unwinding, and a second panic there aborts the process.
if self.finished {
return Vec::new();
}
self.finished = true;
let mut errors: Vec<FsError> = self
.shutdown_items
.iter_mut()
.filter_map(|item| item.shutdown().err().map(FsError::from))
.collect();
if let Err(e) = self.reload_handle.reload_telemetry(vec![], vec![]) {
errors.push(*fs_err!(
ErrorCode::Generic,
"Failed to detach invocation telemetry layers: {}",
e
));
}
errors
}
}
impl Drop for InvocationTracingGuard {
fn drop(&mut self) {
// Shutting the items down here is load-bearing, not a redundant backstop: the file
// and parquet writers do flush via their own handles' `Drop`, but OTLP does not —View on GitHub (pinned to 0267ce9170)