dbt-labs/dbt-core · error
Task should always operate on at least a single node
Error message
Task should always operate on at least a single node
What it means
The telemetry_request method assumes every task it serializes telemetry for operates on at least one dbt node; a task with an empty node list is a construction invariant violation, so the code panics instead of emitting an empty span-tree request. It exists to keep the span-tree telemetry contract (exactly one primary node for single-node tasks) well-defined.
Source
Thrown at crates/dbt-tasks-core/src/task.rs:226
/// Such tasks MUST create similar task-like spans for each underlying node themselves.
///
/// If `skip_reason` is provided, the default implementation applies the same shared skip
/// mapping used on span skip-close callbacks so `NodeEvaluated` starts with a skipped
/// outcome already set.
///
/// # Panics
/// - If task reports zero nodes but doesn't override this method.
/// - If task reports has no phase but doesn't override this method.
fn telemetry_request(
&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);View on GitHub (pinned to 0267ce9170)
Solutions
- Fix the node-selection code so a task is never created with zero nodes, or skip telemetry_request when dbt_nodes() is empty.
- Guard the caller: check that the selection produced at least one node before building the task.
- Log and return a no-op SpanTreeRequest for empty tasks instead of reaching this method.
- Reproduce with the same selector flags and inspect why selection yielded nothing.
Example fix
// before
let node = nodes.next().expect("Task should always operate on at least a single node");
// after
let Some(node) = nodes.next() else { return SpanTreeRequest::use_current(); }; Defensive patterns
Strategy: validation
Validate before calling
if task.dbt_nodes().count() == 0 { return; } // skip telemetry for empty tasks Type guard
fn has_nodes(task: &dyn AnyTask) -> bool { task.dbt_nodes().next().is_some() } Try / catch
let Some(node) = nodes.next() else { return SpanTreeRequest::use_current(); }; Prevention
- Never construct a single-node task from an empty selection; bail out earlier.
- Verify selectors matched resources before instantiating the task.
- Handle the empty-selection case at task creation, not at telemetry time.
When it happens
Trigger: Calling telemetry_request on a task whose dbt_nodes() iterator yields no elements — i.e. the task was created/selected with zero nodes.
Common situations: An upstream selection filter matched nothing but the task was still instantiated; a bug in node selection (e.g. state:modified selectors returning empty while the task proceeds); running a single-node task type with a missing/misconfigured target resource.
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
- Task that doesn't belong to a phase should override telemetr
- when data_type is date, inner must be a TimeConfig
- ModelPropertiesSemanticModelConfig is never finalized direct
- cannot consume EOF
- synthetic relation construction should not fail: {e}
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/532ffeef391475b4.
Report an issue: GitHub.