PyO3/pyo3 · error
set iteration should be infallible
Error message
set iteration should be infallible
What it means
The `Iterator::next` impl for a set's `BoundSetIterator` unwraps the inner `PyResult` with `.expect("set iteration should be infallible")` because iterating a CPython set is guaranteed not to fail. If the underlying `PyIterator` returns an error, this panic fires, signaling interpreter-state corruption or misuse rather than a catchable library error.
Source
Thrown at src/types/set.rs:273
}
/// PyO3 implementation of an iterator for a Python `set` object.
pub struct BoundSetIterator<'py>(Bound<'py, PyIterator>);
impl<'py> BoundSetIterator<'py> {
pub(super) fn new(set: Bound<'py, PySet>) -> Self {
Self(PyIterator::from_object(&set).expect("set should always be iterable"))
}
}
impl<'py> Iterator for BoundSetIterator<'py> {
type Item = Bound<'py, super::PyAny>;
/// Advances the iterator and returns the next value.
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
.map(|result| result.expect("set iteration should be infallible"))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = ExactSizeIterator::len(self);
(len, Some(len))
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.len()
}
}
impl ExactSizeIterator for BoundSetIterator<'_> {
fn len(&self) -> usize {View on GitHub (pinned to ac9b6899d3)
Solutions
- Do not mutate the set (from Rust callbacks or re-entrant Python) while iterating it
- Audit unsafe code holding raw pointers into the set during iteration
- Iterate defensively via `__iter__`/`__next__` with `?` if interpreter state can't be trusted
Example fix
// before
for item in set.iter() { set.add(item)?; } // mutating during iteration
// after
let items: Vec<_> = set.iter().map(|i| i.unbind()).collect();
for item in items { set.add(item)?; } Defensive patterns
Strategy: try-catch
Validate before calling
// snapshot before iterating to avoid mid-iteration mutation issues items = list(py_set)
Type guard
fn is_set(obj: &Bound<'_, PyAny>) -> bool {
obj.downcast::<PySet>().is_ok()
} Try / catch
// fallible iteration pattern instead of the panicking wrapper
let it = set_obj.call_method0("__iter__")?;
loop {
match it.call_method0("__next__") {
Ok(item) => { /* ... */ }
Err(e) if e.is_instance_of::<PyStopIteration>(py) => break,
Err(e) => return Err(e),
}
} Prevention
- Never mutate a set while iterating it (copy first)
- Avoid re-entrant Python calls that touch the set inside the loop
- Audit unsafe code holding raw pointers during iteration
When it happens
Trigger: Calling `.next()` (any `for` loop or `.collect()` over `set.iter()` or `PySet` iteration) when the inner iterator yields `Err` — practically only after unsafe mutation/deallocation of the set during iteration or interpreter corruption.
Common situations: Mutating a set from other (native/callback) code while Rust iterates it, bad refcount handling in embedding code, alternate interpreters with different error behavior.
Related errors
- set should always be iterable
- frozenset iteration should be infallible
- Attaching a thread to the interpreter is prohibited while a
- Cannot attach to the Python interpreter while it is finalizi
- Attaching a thread to the interpreter is currently prohibite
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/33a1205f615275b2.
Report an issue: GitHub.