PyO3/pyo3 · info
seconds overflow
Error message
seconds overflow
What it means
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.
Source
Thrown at src/conversions/time.rs:188
// e.g., -10 seconds should be -1 days + 86390 seconds
let days = total_seconds.div_euclid(SECONDS_PER_DAY);
let seconds = total_seconds.rem_euclid(SECONDS_PER_DAY);
(days, seconds)
} else {
// For positive or exact negative days, use normal division
(
total_seconds / SECONDS_PER_DAY,
total_seconds % SECONDS_PER_DAY,
)
};
let days = days
.try_into()
.map_err(|_| PyOverflowError::new_err("duration out of range for Python timedelta"))?;
PyDelta::new(
py,
days,
seconds.try_into().expect("seconds overflow"),
micro_seconds,
true,
)
}
}
impl FromPyObject<'_, '_> for Duration {
type Error = PyErr;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = PyDelta::TYPE_HINT;
fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
#[cfg(not(Py_LIMITED_API))]
let (days, seconds, microseconds) = {
let delta = ob.cast::<PyDelta>()?;
(
delta.get_days().into(),View on GitHub (pinned to ac9b6899d3)
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.
Example fix
// before (caller) let td: Bound<PyDelta> = my_duration.into_pyobject(py)?.extract()?; // panics only if invariant broken // after (guard against out-of-range durations) let max = Duration::from_secs(86399 * 999999999 + 86399); let d = my_duration.min(max); let td: Bound<PyDelta> = d.into_pyobject(py)?.extract()?;
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the duration fits a Python timedelta (max ~999999999 days)
const MAX: Duration = Duration::from_secs(86399 * 999999999 + 86399);
fn fits_timedelta(d: Duration) -> bool { d <= MAX } Type guard
fn fits_timedelta(d: std::time::Duration) -> bool {
d <= std::time::Duration::from_secs(86399 * 999999999 + 86399)
} Try / catch
use pyo3::exceptions::PyOverflowError;
match result {
Ok(td) => td,
Err(e) if e.is_instance_of::<PyOverflowError>(py) => {
// handle out-of-range duration
...
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- length of buffer fits in Py_ssize_t
- failed to import `datetime` C API
- ignored leap-second, `datetime` does not support leap-second
- Attaching a thread to the interpreter is prohibited while a
- Cannot attach to the Python interpreter while it is finalizi
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/8d69c33344ff6378.
Report an issue: GitHub.