dbt-labs/dbt-core · error

inconsistent park state; actual = {actual}

Error message

inconsistent park state; actual = {actual}

What it means

This custom Parker (mirroring std's park/unpark) uses an atomic `state` (EMPTY/NOTIFIED) plus a condvar/mutex. When park() observes a state value that is neither EMPTY (freshly unparked with a consumed token) nor NOTIFIED, the internal state machine is corrupt and it panics with the unexpected value. This indicates a bug in the park/unpark coordination, not in user data.

Source

Thrown at crates/dbt-runtime/src/park.rs:105

        // Otherwise we need to coordinate going to sleep
        let mut m = self.mutex.lock().unwrap();

        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
            Ok(_) => {}
            Err(NOTIFIED) => {
                // We must read here, even though we know it will be `NOTIFIED`.
                // This is because `unpark` may have been called again since we read
                // `NOTIFIED` in the `compare_exchange` above. We must perform an
                // acquire operation that synchronizes with that `unpark` to observe
                // any writes it made before the call to unpark. To do that we must
                // read from the write it made to `state`.
                let old = self.state.swap(EMPTY, SeqCst);
                debug_assert_eq!(old, NOTIFIED, "park state changed unexpectedly");

                return;
            }
            Err(actual) => panic!("inconsistent park state; actual = {actual}"),
        }

        loop {
            m = self.condvar.wait(m).unwrap();

            if self
                .state
                .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
                .is_ok()
            {
                // got a notification
                return;
            }

            // spurious wakeup, go back to sleep
        }
    }

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Report/minimize the race: this is an internal invariant failure, usually not fixable from user code — capture the `actual` value and a reproduction.
  2. Audit any custom modifications to park.rs or the unpark path for state transitions that skip EMPTY/NOTIFIED.
  3. Verify all state transitions use the documented orderings (SeqCst swaps) and that unpark always sets NOTIFIED before waking the condvar.
  4. As a workaround, pin to a prior runtime version where the park state machine was stable.
Defensive patterns

Strategy: retry

Try / catch

// this is an internal panic, not a catchable error; restart the runtime worker
let result = std::panic::catch_unwind(AssertUnwindSafe(|| worker_loop()));
if result.is_err() {
    eprintln!("park state panic; restarting worker");
    restart_worker();
}

Prevention

When it happens

Trigger: Calling park() when the atomic state holds an unexpected value — caused by concurrent unpark/park racing that violated the state protocol, an unpark without proper NOTIFICATION sequencing, memory-ordering bugs, or the timeout path in park_timeout interleaving with park incorrectly.

Common situations: Very high-contentiation wakeups on the blocking pool; a custom runtime modification breaking the park token protocol; a signal/unpark arriving between the state swap and the mutex acquisition in ways the invariant didn't anticipate; running under sanitizers exposing a pre-existing race.

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/3099f1e07d174674. Report an issue: GitHub.