dbt-labs/dbt-core · error

this should be handled somewhere else

Error message

this should be handled somewhere else

What it means

When a task succeeds, the visitor maps its NodeStatus to downstream propagation actions. The SkippedUpstreamFailed status is contractually converted earlier (it should never appear as a task result at this point), so encountering it in this match means upstream-failure handling was bypassed and the code panics rather than propagating skips incorrectly.

Solutions

  1. Find where the task result status is set and ensure SkippedUpstreamFailed is handled there (mark dependents skipped without calling handle_task_result).
  2. Add an explicit early-return arm for SkippedUpstreamFailed in handle_task_result that performs the skip propagation instead of panicking.
  3. Check custom status transitions for leaked SkippedUpstreamFailed outcomes.
  4. Reproduce with the failing node's unique_id and trace which code path assigned the status.

Example fix

// before
NodeStatus::SkippedUpstreamFailed => unreachable!("this should be handled somewhere else"),

// after
NodeStatus::SkippedUpstreamFailed => self.propagate_skipped(task_idx, dependents, schedule),
Defensive patterns

Strategy: validation

Validate before calling

if result.status == NodeStatus::SkippedUpstreamFailed { return; } // handled by skip propagation elsewhere

Type guard

fn is_success_path_status(s: &NodeStatus) -> bool { !matches!(s, NodeStatus::SkippedUpstreamFailed) }

Try / catch

match status {
    NodeStatus::SkippedUpstreamFailed => return Ok(()), // handled upstream; do not re-process
    other => handle_task_result_inner(other),
}

Prevention

When it happens

Trigger: Calling handle_task_result (from visit or the state-comparison test visitor paths) with a task result whose NodeStatus is SkippedUpstreamFailed — i.e. a status that should have been translated before entering the success-handling match.

Common situations: A scheduling/propagation bug records SkippedUpstreamFailed as the task's own outcome instead of skipping via dependents; custom forks or modified status transitions leak the status into the visitor; state:test flows (reused-model preemption logic) producing this status from a different code path than the main run visitor.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/dbt-tasks-sa/src/visitor.rs:265

                    Vec::new(),
                    if reuse_downstream_tests {
                        // TODO: Unfortunately, we have diverging logic for data tests vs all other node types,
                        // when it comes to upstream being reused. All other node tasks will run and just
                        // report themselves as reused, but for tests we have historically propagated skips
                        // in the visitor. This should be unified eventually.
                        self.propagate_reuse_to_downstream_tests(task_idx, dependents, schedule)
                    } else {
                        Vec::new()
                    },
                ),
                NodeStatus::Succeeded
                | NodeStatus::SucceededWithWarning
                | NodeStatus::TestPassed
                | NodeStatus::StaticallyCheckedDataTest
                | NodeStatus::TestWarned
                | NodeStatus::NoOp => (Vec::new(), Vec::new()),
                NodeStatus::SkippedUpstreamFailed => {
                    unreachable!("this should be handled somewhere else")
                }
            },
            Err(_) => (
                self.propagate_failure(task_idx, dependents, schedule),
                Vec::new(),
            ),
        }
    }
}

// Returns true when the failing task is a model with `on_error: continue`.
//
// Phase gating: honored for Render and Run failures, but **not** Analyze.
// Analyze produces the type/binding facts that `--static-analysis strict`
// downstreams consume, so upstream Analyze failures must still propagate.
//
// TODO: The correct rule is per-downstream — propagate Render/Analyze
// failure only to downstreams whose `static_analysis` is `strict`. The

View on GitHub (pinned to 0267ce9170)