rayon-rs/rayon · error
unzip consumers didn't execute!
Error message
unzip consumers didn't execute!
What it means
`UnzipConsumer::drive_unindexed` returns the left consumer's result via `Option::expect`; it panics if the right side's `par_extend` never drove the iterator, leaving no result for the left. This indicates the unzip consumer protocol was violated internally.
Solutions
- Upgrade rayon to the latest version
- If using a custom Consumer/UnindexedConsumer impl, ensure it drives all fed items in `par_extend`
- Work around by splitting the iterator: `let (a, b): (Vec<_>, Vec<_>) = iter.map(|x| (f(x), g(x))).unzip();` with collected intermediates
- File a minimal repro on the rayon issue tracker
Example fix
// before custom_unzip_consumer_drive(iter); // protocol-violating consumer // after let (left, right): (Vec<_>, Vec<_>) = iter.unzip();
Defensive patterns
Strategy: try-catch
Try / catch
let r = catch_unwind(AssertUnwindSafe(|| iter.unzip()));
if r.is_err() { /* fall back to sequential unzip */ } Prevention
- Use stock rayon consumers, not custom ones, with unzip
- Upgrade rayon when hitting consumer-contract panics
- Add a sequential unzip fallback path
When it happens
Trigger: Unzipping an unindexed parallel iterator where the right consumer's `par_extend` fails to actually consume the items (internal consumer contract violation), so `result` remains None.
Common situations: Rare; arises from rayon bugs or custom `Consumer` implementations used with `unzip` that don't drive the fed iterator.
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
- FIFO is empty
- range start should be <= length
- range end should be <= length
- range start should be <= range end
- The global thread pool has not been initialized.
AI-assisted analysis of rayon-rs/rayon@ee0a00bdb1 (2026-09-07).
Data as JSON: /api/errors/af01478e9aa1fe0d.
Report an issue: GitHub.
Appendix: source
Thrown at src/iter/unzip.rs:225
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let mut result = None;
{
// Now it's time to find the consumer for type `B`
let iter = UnzipB {
base: self.base,
op: self.op,
left_consumer: consumer,
left_result: &mut result,
};
self.b.par_extend(iter);
}
// NB: If for some reason `b.par_extend` doesn't actually drive the
// iterator, then we won't have a result for the left side to return
// at all. We can't fake an arbitrary consumer's result, so panic.
result.expect("unzip consumers didn't execute!")
}
fn opt_len(&self) -> Option<usize> {
if OP::indexable() {
self.base.opt_len()
} else {
None
}
}
}
/// A fake iterator to intercept the `Consumer` for type `B`.
struct UnzipB<'r, I, OP, CA>
where
I: ParallelIterator,
OP: UnzipOp<I::Item>,
CA: UnindexedConsumer<OP::Left>,
{View on GitHub (pinned to ee0a00bdb1)