{"record":{"id":"9078bb8084c627a2","repo":"PyO3/pyo3","slug":"unknown-return-value-from-pydict-setdefaultref-x","errorCode":null,"errorMessage":"Unknown return value from PyDict_SetDefaultRef: {x}","messagePattern":"Unknown return value from PyDict_SetDefaultRef: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/dict.rs","lineNumber":478,"sourceCode":"        inner(\n            self,\n            key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),\n            default_value\n                .into_pyobject_or_pyerr(py)?\n                .into_any()\n                .as_borrowed(),\n            py,\n        )\n    }\n}\n\nfn setdefault_result_from_nonerror_return_code(code: PyResult<core::ffi::c_int>) -> PyResult<bool> {\n    match code? {\n        // inserted\n        0 => Ok(true),\n        // not inserted\n        1 => Ok(false),\n        x => panic!(\"Unknown return value from PyDict_SetDefaultRef: {x}\"),\n    }\n}\n\nimpl<'a, 'py> Borrowed<'a, 'py, PyDict> {\n    /// Iterates over the contents of this dictionary without incrementing reference counts.\n    ///\n    /// # Safety\n    /// It must be known that this dictionary will not be modified during iteration,\n    /// for example, when parsing arguments in a keyword arguments dictionary.\n    pub(crate) unsafe fn iter_borrowed(self) -> BorrowedDictIter<'a, 'py> {\n        BorrowedDictIter::new(self)\n    }\n}\n\nfn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {\n    #[cfg(any(PyPy, GraalPy, Py_LIMITED_API, Py_GIL_DISABLED))]\n    unsafe {\n        ffi::PyDict_Size(dict.as_ptr())","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/dict.rs#L460-L496","documentation":"setdefault_result_from_nonerror_return_code maps PyDict_SetDefaultRef's documented return codes (0 = inserted, 1 = already present) to bool, and panics on any other value. Since SetDefaultRef should never return anything else when no exception is set, this panic signals an unexpected C-API contract violation (interpreter bug, FFI mismatch, or wrong Python version).","triggerScenarios":"Calling PyDict::setdefault (the PyDict_SetDefaultRef-backed path, Python 3.13+) where the C API returns a code other than 0/1 — e.g. an ffi signature mismatch or a non-conforming interpreter.","commonSituations":"Using a Python build whose PyDict_SetDefaultRef behaves differently than declared; mixed-version headers/ABI; custom CPython forks. Rare in practice.","solutions":["Verify the Python headers/ABI in use match the running interpreter version","Update PyO3 to the latest patch release for 3.13+ SetDefaultRef fixes","Check that PyDict_SetDefaultRef returns PySet_Contains-style codes and no exception was set; inspect via PyErr_Occurred before panicking","As a workaround, use a non-SetDefaultRef code path (older setdefault implementation) or pin a supported Python version"],"exampleFix":"// before\nlet existed = dict.setdefault(key, value)?; // panics on unexpected code\n// after\nif unsafe { ffi::PyErr_Occurred().is_null() } {\n    let existed = dict.setdefault(key, value)?;\n} else { /* handle PyErr */ }","handlingStrategy":"validation","validationCode":"if unsafe { pyo3::ffi::PyErr_Occurred() }.is_null() {\n    // SetDefaultRef path is safe to attempt\n} else { /* clear/handle existing exception first */ }","typeGuard":null,"tryCatchPattern":"let r = std::panic::catch_unwind(|| dict.setdefault(key, value)); match r { Ok(res) => res, Err(_) => { /* fallback to contains_key + get_item */ } }","preventionTips":["Keep Python header/ABI versions in sync with the running interpreter","Pin supported Python versions for PyDict_SetDefaultRef paths (3.13+)","Update PyO3 when C-API contract fixes ship"],"tags":["pyo3","dict","c-api","panic","python-3-13"],"backgroundTag":"unexpected-c-api-return-code","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"}