{"record":{"id":"95644014ae5d4217","repo":"PyO3/pyo3","slug":"0-n-12","errorCode":null,"errorMessage":"0 < N <= 12","messagePattern":"0 < N <= 12","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/tuple.rs","lineNumber":954,"sourceCode":"                    // SAFETY: index guaranteed in bounds by the length check\n                    unsafe { t.get_borrowed_item_unchecked($n) }\n                        .extract::<$T>()\n                        .map_err(Into::into)?,\n                )+))\n            } else {\n                Err(wrong_tuple_length(t, $length))\n            }\n        }\n    }\n});\n\nfn array_into_tuple<'py, const N: usize>(\n    py: Python<'py>,\n    array: [Bound<'py, PyAny>; N],\n) -> Bound<'py, PyTuple> {\n    #[cfg(not(RustPython))]\n    unsafe {\n        let ptr = ffi::PyTuple_New(N.try_into().expect(\"0 < N <= 12\"));\n        let tup = ptr.assume_owned(py).cast_into_unchecked();\n        for (index, obj) in array.into_iter().enumerate() {\n            #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]\n            ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr());\n            #[cfg(any(Py_LIMITED_API, PyPy, GraalPy))]\n            ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr());\n        }\n        tup\n    }\n\n    // SAFETY: array is layout compatible with *const *mut crate::PyObject\n    // and does not steal the bound reference.\n    #[cfg(RustPython)]\n    unsafe {\n        ffi::PyTuple_FromArray(array.as_ptr().cast(), N.try_into().expect(\"0 < N <= 12\"))\n            .assume_owned(py)\n            .cast_into_unchecked()\n    }","sourceCodeStart":936,"sourceCodeEnd":972,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/tuple.rs#L936-L972","documentation":"`array_into_tuple` converts a fixed-size Rust array of `Bound<PyAny>` into a `PyTuple`, asserting `0 < N <= 12` when converting `N` to `Py_ssize_t` for `PyTuple_New`. The bound exists because pyo3's `IntoPyTuple` impls are only generated for arrays of size 1–12; `N == 0` would create a null-item tuple path and larger arrays aren't supported by the trait impls.","triggerScenarios":"Calling `.into_py_tuple()`/`PyTuple::new` paths on a `[Bound<PyAny>; N]` array with `N == 0` (e.g. `[]` with inferred element type `Bound<PyAny>`) or `N > 12`.","commonSituations":"Passing an empty array literal to tuple conversion after a `Vec::try_into()`, or upgrading pyo3 code that previously used a Vec and now uses a >12-element array.","solutions":["Use a `Vec<Bound<PyAny>>` or `&[Bound<PyAny>]` instead of a >12-element array","For empty arrays, use `PyTuple::empty(py)` explicitly","Ensure the array element type is correct so `try_into` infers N within 1..=12"],"exampleFix":"// before: 13-element array\nlet tup = array13.into_pytuple(py);\n// after\nlet tup = PyTuple::new(py, array13.as_slice())?;","handlingStrategy":"validation","validationCode":"const fn array_size_ok<const N: usize>() -> bool { N > 0 && N <= 12 }\n// compile-time guard\nconst _: () = assert!(array_size_ok::<N>());","typeGuard":"fn convertible_array<const N: usize>(a: [Bound<'_, PyAny>; N]) -> Option<[Bound<'_, PyAny>; N]> {\n    (1..=12).contains(&N).then_some(a)\n}","tryCatchPattern":"// runtime guard before conversion\nlet tup = if N == 0 { PyTuple::empty(py) } else { array.into_pytuple(py) };","preventionTips":["Keep arrays within 1..=12 elements for IntoPyTuple","Use Vec/slices for arbitrary lengths","Use PyTuple::empty for the empty case"],"tags":["pyo3","panic","tuple","array"],"backgroundTag":"tuple-array-size-limit","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}