{"record":{"id":"8d69c33344ff6378","repo":"PyO3/pyo3","slug":"seconds-overflow","errorCode":null,"errorMessage":"seconds overflow","messagePattern":"seconds overflow","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src/conversions/time.rs","lineNumber":188,"sourceCode":"            // e.g., -10 seconds should be -1 days + 86390 seconds\n            let days = total_seconds.div_euclid(SECONDS_PER_DAY);\n            let seconds = total_seconds.rem_euclid(SECONDS_PER_DAY);\n            (days, seconds)\n        } else {\n            // For positive or exact negative days, use normal division\n            (\n                total_seconds / SECONDS_PER_DAY,\n                total_seconds % SECONDS_PER_DAY,\n            )\n        };\n        let days = days\n            .try_into()\n            .map_err(|_| PyOverflowError::new_err(\"duration out of range for Python timedelta\"))?;\n\n        PyDelta::new(\n            py,\n            days,\n            seconds.try_into().expect(\"seconds overflow\"),\n            micro_seconds,\n            true,\n        )\n    }\n}\n\nimpl FromPyObject<'_, '_> for Duration {\n    type Error = PyErr;\n\n    #[cfg(feature = \"experimental-inspect\")]\n    const INPUT_TYPE: PyStaticExpr = PyDelta::TYPE_HINT;\n\n    fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {\n        #[cfg(not(Py_LIMITED_API))]\n        let (days, seconds, microseconds) = {\n            let delta = ob.cast::<PyDelta>()?;\n            (\n                delta.get_days().into(),","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/conversions/time.rs#L170-L206","documentation":"When converting a std Duration to a Python timedelta, PyO3 splits it into days and remaining seconds, then casts seconds to the ffi type with expect(\"seconds overflow\"). The range check happens earlier via try_into on the total (which raises PyOverflowError \"duration out of range\"), so this expect documents the invariant that the remaining seconds (always < 86400) fit the target integer type.","triggerScenarios":"Should be unreachable through the public Duration::into_pyobject path: it would require the earlier out-of-range check to pass while the sub-day seconds component still overflows i32/c_long, which cannot happen mathematically.","commonSituations":"Could only surface on a platform where c_long is narrower than expected or if the pre-check logic changed; users converting very large Durations get a PyOverflowError instead, not this panic.","solutions":["No user action required; oversized durations already raise PyOverflowError('duration out of range for Python timedelta') which you should catch.","Clamp or validate Duration inputs (keep them under ~2^63 microseconds / timedelta's max) before returning them to Python.","Report to pyo3 if the expect fires for an in-range duration."],"exampleFix":"// before (caller)\nlet td: Bound<PyDelta> = my_duration.into_pyobject(py)?.extract()?; // panics only if invariant broken\n// after (guard against out-of-range durations)\nlet max = Duration::from_secs(86399 * 999999999 + 86399);\nlet d = my_duration.min(max);\nlet td: Bound<PyDelta> = d.into_pyobject(py)?.extract()?;","handlingStrategy":"try-catch","validationCode":"// Ensure the duration fits a Python timedelta (max ~999999999 days)\nconst MAX: Duration = Duration::from_secs(86399 * 999999999 + 86399);\nfn fits_timedelta(d: Duration) -> bool { d <= MAX }","typeGuard":"fn fits_timedelta(d: std::time::Duration) -> bool {\n    d <= std::time::Duration::from_secs(86399 * 999999999 + 86399)\n}","tryCatchPattern":"use pyo3::exceptions::PyOverflowError;\nmatch result {\n    Ok(td) => td,\n    Err(e) if e.is_instance_of::<PyOverflowError>(py) => {\n        // handle out-of-range duration\n        ...\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Clamp or reject Durations beyond timedelta's range before conversion.","Catch PyOverflowError when converting untrusted durations in #[pyfunction]s.","Prefer i64 microsecond bounds checks in Rust before crossing into Python."],"tags":["pyo3","datetime","timedelta","duration","unreachable"],"backgroundTag":"duration-out-of-range","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"}