rayon-rs/rayon · error
FIFO is empty
Error message
FIFO is empty
What it means
Rayon's work-stealing queue panicked because `steal()` repeatedly returned `Steal::Empty` while trying to execute a job as FIFO. This is an internal invariant: a job that reached this path must exist in a queue, so an empty steal indicates a lifecycle bug or misuse of unsafe APIs around job references.
Solutions
- Upgrade rayon/rayon-core to the latest patched version
- Reduce concurrent use of low-level rayon-core internals (job refs, registries)
- Reproduce with a minimal test and file an issue on the rayon repo
- As a workaround, switch to a stable thread-pool crate (e.g. std::thread scoped threads)
Example fix
// before: relying on internal job API
let job_ref = unsafe { ... };
job_ref.execute();
// after: use public rayon API
rayon::scope(|s| { s.spawn(|_| work()); }); Defensive patterns
Strategy: try-catch
Try / catch
// Rust: panics are not catchable at normal call sites; isolate risky work catch_unwind(AssertUnwindSafe(|| rayon::join(work_a, work_b)))
Prevention
- Pin to patched rayon versions
- Avoid unsafe integration with rayon-core internals
- Run stress tests to surface scheduler races early
When it happens
Trigger: A job reference is executed via `JobRef::execute` (internal rayon-core path) while all worker queues report empty; typically reached only through internal scheduler bugs or incorrect custom unsafe integration with rayon's job API.
Common situations: Rare; seen in rayon-core internal scheduler races or when downstream code misuses unsafe job/registry APIs. End users hitting this are usually on a buggy rayon version.
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
- owner thread
- unzip consumers didn't execute!
- range start should be <= length
- range end should be <= length
- range start should be <= range end
AI-assisted analysis of rayon-rs/rayon@ee0a00bdb1 (2026-09-07).
Data as JSON: /api/errors/6ac6592f2e35b4b2.
Report an issue: GitHub.
Appendix: source
Thrown at rayon-core/src/job.rs:272
pub(super) unsafe fn push(&self, job_ref: JobRef) -> JobRef {
// A little indirection ensures that spawns are always prioritized in FIFO order. The
// jobs in a thread's deque may be popped from the back (LIFO) or stolen from the front
// (FIFO), but either way they will end up popping from the front of this queue.
self.inner.push(job_ref);
unsafe { JobRef::new(self) }
}
}
impl Job for JobFifo {
unsafe fn execute(this: *const ()) {
unsafe {
// We "execute" a queue by executing its first job, FIFO.
let this = &*(this as *const Self);
loop {
match this.inner.steal() {
Steal::Success(job_ref) => break job_ref.execute(),
Steal::Empty => panic!("FIFO is empty"),
Steal::Retry => std::hint::spin_loop(),
}
}
}
}
}
View on GitHub (pinned to ee0a00bdb1)