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

  1. Do not mutate the set (from Rust callbacks or re-entrant Python) while iterating it
  2. Audit unsafe code holding raw pointers into the set during iteration
  3. 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

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


AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05). Data as JSON: /api/errors/33a1205f615275b2. Report an issue: GitHub.