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
- Report/minimize the race: this is an internal invariant failure, usually not fixable from user code — capture the `actual` value and a reproduction.
- Audit any custom modifications to park.rs or the unpark path for state transitions that skip EMPTY/NOTIFIED.
- Verify all state transitions use the documented orderings (SeqCst swaps) and that unpark always sets NOTIFIED before waking the condvar.
- 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
- Do not modify park/unpark state transitions without keeping the EMPTY/NOTIFIED protocol intact.
- Keep all Parker state swaps at SeqCst ordering as the implementation expects.
- Stress-test with many concurrent unparks/timeouts to surface races early.
- If reproducible, file a bug with the printed `actual` state value and a minimal reproduction.
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
- inconsistent park_timeout state; actual = {actual}
- `EnterGuard` values dropped out of order. Guards returned by
- {e} {:?}
- {e}
- inconsistent park_timeout state: {n}
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/3099f1e07d174674.
Report an issue: GitHub.