astral-sh/ruff · critical

tried to get QoS of thread which has opted out of QoS

Error message

tried to get QoS of thread which has opted out of QoS

What it means

This is the read-side counterpart of the QoS invariant panic: `get_current_thread_qos_class` calls a macOS API to query the thread's QoS class and receives the 'unspecified' class, which means the thread has opted out of QoS (usually via manual scheduling APIs). Because ty assumes it exclusively uses QoS-compatible APIs, this state is treated as a broken invariant and panics.

Source

Thrown at crates/ty_server/src/server/schedule/thread/priority.rs:267

            let errno = unsafe { *libc::__error() };
            unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
        }

        match qos_class_raw {
            libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
            libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
            libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set
            libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
            libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),

            libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
                // Using manual scheduling APIs causes threads to “opt out” of QoS.
                // At this point they become incompatible with QoS,
                // and as such have the “unspecified” QoS class.
                //
                // Panic instead of returning an error
                // to maintain the invariant that we only use QoS APIs.
                panic!("tried to get QoS of thread which has opted out of QoS")
            }
        }
    }

    pub(super) fn thread_priority_to_qos_class(priority: ThreadPriority) -> QoSClass {
        match priority {
            ThreadPriority::Worker => QoSClass::Utility,
            ThreadPriority::LatencySensitive => QoSClass::UserInitiated,
        }
    }
}

// FIXME: Windows has QoS APIs, we should use them!
#[cfg(not(target_vendor = "apple"))]
mod imp {
    use super::ThreadPriority;

    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Ensure the server's threads avoid `pthread_setschedparam` and other APIs that opt out of QoS.
  2. Run ty's scheduler on freshly created, default-scheduling threads.
  3. Reconfigure the embedding application to use QoS APIs (e.g. pthread_set_qos_class_self_np / dispatch QoS) instead of legacy priority APIs.
Defensive patterns

Strategy: try-catch

Try / catch

// Panic occurs inside ty; isolate it at the thread boundary:
std::panic::catch_unwind(|| ty_scheduler::current_priority())
    .map_err(|_| "QoS unavailable: run on a thread that has not opted out")

Prevention

When it happens

Trigger: Calling into ty's scheduler (priority handling on macOS) from a thread that previously used manual scheduling APIs, so the queried QoS class is QOS_CLASS_UNSPECIFIED.

Common situations: Embedding the ty LSP server in applications or plugins that set thread priorities with pthread APIs; running the server inside native thread pools with custom scheduling on macOS.

Related errors


AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/f23bcbcb80c0234d. Report an issue: GitHub.