PyO3/pyo3 · info
frozenset should always be iterable
Error message
frozenset should always be iterable
What it means
PyO3 wraps a `frozenset` iterator as `BoundFrozenSetIterator`, constructing a `PyIterator` from the set. The `.expect("frozenset should always be iterable")` asserts a CPython invariant: frozensets are always iterable, so `PyIterator::from_object` should never fail. Reaching this panic means an object masquerading as, or corrupting, a `frozenset` was passed, or the type object's `tp_iter` was patched.
Source
Thrown at src/types/frozenset.rs:227
}
}
impl<'py> IntoIterator for &Bound<'py, PyFrozenSet> {
type Item = Bound<'py, PyAny>;
type IntoIter = BoundFrozenSetIterator<'py>;
/// Returns an iterator of values in this set.
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// PyO3 implementation of an iterator for a Python `frozenset` object.
pub struct BoundFrozenSetIterator<'py>(Bound<'py, PyIterator>);
impl<'py> BoundFrozenSetIterator<'py> {
pub(super) fn new(set: Bound<'py, PyFrozenSet>) -> Self {
Self(PyIterator::from_object(&set).expect("frozenset should always be iterable"))
}
}
impl<'py> Iterator for BoundFrozenSetIterator<'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("frozenset iteration should be infallible"))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = ExactSizeIterator::len(self);
(len, Some(len))
}
View on GitHub (pinned to ac9b6899d3)
Solutions
- Verify with `isinstance(x, frozenset)` (not just a type-pointer match) before passing objects where the panic occurs
- Remove any monkeypatching of builtin frozenset types/slots
- Upgrade pyo3 and reproduce with a minimal snippet; report to PyO3 if hit in vanilla CPython
- Check for C extension corruption or mixed Python runtimes in the process
Example fix
# before fake = Mock(spec=frozenset) # spec is not a real frozenset rust_iter_fn(fake) # after real = frozenset(fake) rust_iter_fn(real)
Defensive patterns
Strategy: type-guard
Validate before calling
# Python: pass real frozensets only
isinstance(value, frozenset) or raise TypeError('expected frozenset') Type guard
def is_real_frozenset(value) -> bool:
return type(value) is frozenset Prevention
- Use type(x) is frozenset for strict checks into Rust APIs
- Never mock/patch builtin frozenset types
- Convert with frozenset(value) before crossing into Rust
- Report any reproduction on vanilla CPython to PyO3
When it happens
Trigger: Passing an object whose type claims to be a frozenset (subclass or mocked `Py_TYPE` swap) but whose iterator cannot be created; monkeypatching `frozenset` type slots; memory corruption at interpreter teardown.
Common situations: Test doubles / mocks that impersonate frozensets; C-API-level tampering with builtin types (some exotic runtime patches); essentially unreachable in normal code — mostly encountered in corrupted environments.
Related errors
- frozenset iteration should be infallible
- Neither abi3 or abi3t features are enabled
- Cannot target an abi3t version below {MINIMUM_SUPPORTED_VERS
- failed to run the Python interpreter at {}: {}
- Python script failed
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/793bfc43f888a086.
Report an issue: GitHub.