dbt-labs/dbt-core · error

inconsistent park_timeout state; actual = {actual}

Error message

inconsistent park_timeout state; actual = {actual}

What it means

The timed variant of the custom park: park_timeout() swaps the atomic state to EMPTY and requires the previous value to be NOTIFIED (a pending unpark token) or EMPTY (no token, spurious path handled above). Any other observed value means the park state machine is inconsistent, so it panics with the unexpected state.

Source

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

            return;
        }

        if dur == Duration::from_millis(0) {
            return;
        }

        let m = self.mutex.lock().unwrap();

        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
            Ok(_) => {}
            Err(NOTIFIED) => {
                // We must read again here, see `park`.
                let old = self.state.swap(EMPTY, SeqCst);
                debug_assert_eq!(old, NOTIFIED, "park state changed unexpectedly");

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

        // Wait with a timeout, and if we spuriously wake up or otherwise wake up
        // from a notification, we just want to unconditionally set the state back to
        // empty, either consuming a notification or un-flagging ourselves as
        // parked.
        let (_m, _result) = self.condvar.wait_timeout(m, dur).unwrap();

        match self.state.swap(EMPTY, SeqCst) {
            NOTIFIED => {} // got a notification, hurray!
            PARKED => {}   // no notification, alas
            n => panic!("inconsistent park_timeout state: {n}"),
        }
    }

    fn unpark(&self) {
        // To ensure the unparked thread will observe any writes we made before
        // this call, we must perform a release operation that `park` can

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Treat as a runtime bug: capture `actual` and reproduce; this panic is not actionable from caller code.
  2. Review park/unpark/park_timeout transitions to guarantee state is only ever EMPTY or NOTIFIED.
  3. Ensure unpark consumes exactly one token and cannot set state while park_timeout is mid-transition.
  4. Downgrade or patch to a runtime version without the park race; add stress tests with timeouts under contention.
Defensive patterns

Strategy: retry

Try / catch

// internal invariant panic: guard the runtime process, not the call
let result = std::panic::catch_unwind(AssertUnwindSafe(|| timed_wait(dur)));
if result.is_err() {
    eprintln!("park_timeout state panic; resetting parker and retrying");
    reset_parker_and_retry(dur);
}

Prevention

When it happens

Trigger: park_timeout() observing a corrupted or unexpected atomic state — concurrent unpark racing with the timeout path, a double-unpark that left state outside the EMPTY/NOTIFIED domain, custom state values introduced by modifications, or a memory-ordering bug in the park implementation.

Common situations: Timed blocking-pool waits under heavy timeout churn; races between a timeout firing and an unpark token being consumed; code paths where park() and park_timeout() interleave on the same Parker with custom state values.

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