dbt-labs/dbt-core · error

Task that doesn't belong to a phase should override telemetr

Error message

Task that doesn't belong to a phase should override telemetry_tree method

What it means

For single-node tasks, telemetry_request needs a TaskPhase to label the span event; phases are obtained via task_phase(), which is only None for task kinds that don't participate in a phase. Such tasks are contractually required to override telemetry_tree with their own span logic, so reaching the None branch here means a phase-less task type was routed through the default telemetry path instead.

Source

Thrown at crates/dbt-tasks-core/src/task.rs:236

        &self,
        in_dir: &Path,
        out_dir: &Path,
        skip_reason: Option<&SkipReason>,
    ) -> SpanTreeRequest<FsResult<NodeStatus>, SkipReason> {
        let mut nodes = self.dbt_nodes().into_iter();

        let Some(node) = nodes.next() else {
            unreachable!("Task should always operate on at least a single node");
        };

        if nodes.next().is_some() {
            // task for multiple nodes use current span as task span.
            // Task inner code should create actual spans for themselves.
            return SpanTreeRequest::use_current();
        }

        let Some(task_phase) = self.task_phase() else {
            unreachable!(
                "Task that doesn't belong to a phase should override telemetry_tree method"
            );
        };

        let phase = task_phase.into();
        let mut attrs = node.get_node_evaluated_event(phase, in_dir, out_dir);
        if let Some(skip_reason) = skip_reason {
            update_node_outcome_from_skip_reason(&mut attrs, skip_reason);
        }

        let task_span_level = match phase {
            ExecutionPhase::Run | ExecutionPhase::Compare => SpanLevel::Info,
            _ => SpanLevel::Debug,
        };
        let mut builder = SpanTreeRequest::builder(
            task_span_level,
            attrs.into(),
            Some(Box::new(task_span_on_close)),

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Override telemetry_tree() on the offending task type to supply its own span-tree request.
  2. Give the task a TaskPhase via task_phase() if it logically belongs to an existing phase.
  3. Route phase-less tasks away from the default telemetry_request path in the dispatcher.
  4. Search for newly added Task implementations missing the telemetry_tree override.

Example fix

// before
impl MyTask { /* no telemetry_tree override */ }

// after
fn telemetry_tree(&self) -> SpanTreeRequest<...> {
    SpanTreeRequest::use_current() // custom span logic for phase-less task
}
Defensive patterns

Strategy: validation

Validate before calling

if task.task_phase().is_none() && !task.overrides_telemetry_tree() { /* route to custom span path */ }

Type guard

fn needs_phase(task: &dyn AnyTask) -> bool { task.task_phase().is_some() }

Prevention

When it happens

Trigger: Calling telemetry_request on a single-node task whose task_phase() returns None because the task type neither belongs to a phase (build/run/test/etc.) nor overrides telemetry_tree.

Common situations: Adding a new task kind (e.g. a utility or docs task) and forgetting to override telemetry_tree; changing a task's phase assignment so it no longer maps to a phase while still inheriting the base implementation.

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


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/9817dd30ab83fa64. Report an issue: GitHub.