PyO3/pyo3 · error

failed to convert tuple to list

Error message

failed to convert tuple to list

What it means

`ToPyObject for PyTuple`'s `to_list` converts the tuple to a Python list via its sequence protocol and asserts success, since any tuple must be convertible to a list. The panic fires if `PySequence::to_list` returns `Err`, meaning the CPython sequence conversion failed — an interpreter invariant violation, not expected user error.

Source

Thrown at src/types/tuple.rs:340

    fn index<V>(&self, value: V) -> PyResult<usize>
    where
        V: IntoPyObject<'py>,
    {
        self.as_sequence().index(value)
    }

    fn iter(&self) -> BoundTupleIterator<'py> {
        BoundTupleIterator::new(self.clone())
    }

    fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
        self.as_borrowed().iter_borrowed()
    }

    fn to_list(&self) -> Bound<'py, PyList> {
        self.as_sequence()
            .to_list()
            .expect("failed to convert tuple to list")
    }
}

impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
    fn get_borrowed_item(self, index: usize) -> PyResult<Borrowed<'a, 'py, PyAny>> {
        unsafe {
            ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t)
                .assume_borrowed_or_err(self.py())
        }
    }

    /// # Safety
    ///
    /// See `get_item_unchecked` in `PyTupleMethods`.
    unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
        cfg_select! {
            // SAFETY: caller has upheld the safety contract
            not(any(Py_LIMITED_API, PyPy, GraalPy)) => unsafe {

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Replace direct `to_list` with `PyList::new(py, tuple.iter())` and handle errors via `?`
  2. Verify interpreter support/version for the sequence conversion API
  3. Report a reproducer to pyo3 if it occurs on stock CPython

Example fix

// before
let list = tuple.to_list();
// after
let list = PyList::new(py, tuple.iter().map(|o| o.clone().unbind()))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the object is a real tuple before conversion
if not isinstance(t, tuple):
    raise TypeError("expected tuple")

Type guard

fn is_tuple(obj: &Bound<'_, PyAny>) -> bool {
    obj.downcast::<PyTuple>().is_ok()
}

Try / catch

// fallible conversion instead of the panicking to_list
let list = PyList::new(py, tup.iter().map(|o| o.clone().unbind()))?;

Prevention

When it happens

Trigger: Calling `to_list` on a `Bound<PyTuple>` (including via `tuple.to_object`/conversion paths) when `PySequence_List`-style conversion errors — practically only with corrupted tuple objects or a non-CPython interpreter whose sequence protocol diverges.

Common situations: Alternate interpreters (RustPython/GraalPy/PyPy) with incomplete sequence APIs, or embedding code that corrupted the tuple object.

Related errors


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