PyO3/pyo3 · error

overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET

Error message

overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET

What it means

This panic comes from a compile-time-checked `checked_add` in pyo3's `with_vectorcall_arguments_offset`, which adds CPython's `PY_VECTORCALL_ARGUMENTS_OFFSET` flag to the argument count before calling the vectorcall protocol. The `.expect` fires only if `n + PY_VECTORCALL_ARGUMENTS_OFFSET` overflows `size_t`, i.e. the caller supplied an astronomically large argument count. It is a defensive guard against future `size_t` narrowing, not a condition normal code reaches.

Source

Thrown at src/types/tuple.rs:981

    }

    // SAFETY: array is layout compatible with *const *mut crate::PyObject
    // and does not steal the bound reference.
    #[cfg(RustPython)]
    unsafe {
        ffi::PyTuple_FromArray(array.as_ptr().cast(), N.try_into().expect("0 < N <= 12"))
            .assume_owned(py)
            .cast_into_unchecked()
    }
}

/// Add `PY_VECTORCALL_ARGUMENTS_OFFSET` to the given number, checking for overflow at compile time.
///
/// Guarantees that we don't accidentally overflow a `size_t` should this get changed in the future.
#[cfg(all(not(any(PyPy, GraalPy)), any(not(Py_LIMITED_API), Py_3_12)))]
const fn with_vectorcall_arguments_offset(n: size_t) -> size_t {
    n.checked_add(ffi::PY_VECTORCALL_ARGUMENTS_OFFSET)
        .expect("overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET")
}

tuple_conversion!(1, (0, T0));
tuple_conversion!(2, (0, T0), (1, T1));
tuple_conversion!(3, (0, T0), (1, T1), (2, T2));
tuple_conversion!(4, (0, T0), (1, T1), (2, T2), (3, T3));
tuple_conversion!(5, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4));
tuple_conversion!(6, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));
tuple_conversion!(
    7,
    (0, T0),
    (1, T1),
    (2, T2),
    (3, T3),
    (4, T4),
    (5, T5),
    (6, T6)
);

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Audit the code computing the number of arguments for underflow or garbage values before it reaches pyo3's call machinery
  2. Reduce the number of arguments being passed (chunk large argument lists)
  3. If seen in a pyo3 version update, report it — it indicates the PY_VECTORCALL_ARGUMENTS_OFFSET constant changed incompatibly with the guard

Example fix

// before
let n = args.len() as size_t; // possibly garbage
pyo3::ffi::call with with_vectorcall_arguments_offset(n)
// after
assert!(args.len() < isize::MAX as usize, "implausible argument count");
let n = args.len() as size_t;
Defensive patterns

Strategy: validation

Validate before calling

fn safe_vectorcall_count(n: usize) -> Option<usize> {
    n.checked_add(1usize << 63) // PY_VECTORCALL_ARGUMENTS_OFFSET on 64-bit
}
// call site: assert!(safe_vectorcall_count(args.len()).is_some(), "argument count overflows");

Type guard

fn plausible_arg_count(n: usize) -> bool {
    n < (1usize << 62)
}

Prevention

When it happens

Trigger: Calling into pyo3's tuple conversion macro (`tuple_conversion!`) or vectorcall machinery with an argument count `n` so large that adding `PY_VECTORCALL_ARGUMENTS_OFFSET` (a huge sentinel bit value like `1 << 63` on 64-bit) overflows `size_t` — effectively only with counts near `usize::MAX`.

Common situations: Essentially never hit in real applications; would require a bug elsewhere (uninitialized/garbage length, integer underflow producing a near-`usize::MAX` count) passed into pyo3's C-call wrappers, or a future change of the offset constant tripping the compile-time guard.

Related errors


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