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

  1. No user action required; oversized durations already raise PyOverflowError('duration out of range for Python timedelta') which you should catch.
  2. Clamp or validate Duration inputs (keep them under ~2^63 microseconds / timedelta's max) before returning them to Python.
  3. 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

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


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