astral-sh/ruff · error

Should only ever pass a positive integer to `from_nonnegativ

Error message

Should only ever pass a positive integer to `from_nonnegative_i32`

What it means

from_nonnegative_i32 converts a non-negative i32 subscript index to usize for tuple/sequence element lookup; it carries a debug_assert!(index >= 0) and a release-mode expect because any non-negative i32 converts losslessly into a usize of at least 32 bits. The panic means a negative index reached a path that promises non-negative indices (from_negative_i32 exists for the negative case).

Source

Thrown at crates/ty_python_semantic/src/subscript.rs:29

pub(crate) struct OutOfBoundsError;

pub(crate) trait PyIndex<'db> {
    type Item: 'db;

    fn py_index(
        self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        index: i32,
    ) -> Result<Self::Item, OutOfBoundsError>;
}

fn from_nonnegative_i32(index: i32) -> usize {
    static_assertions::const_assert!(usize::BITS >= 32);
    debug_assert!(index >= 0);

    usize::try_from(index)
        .expect("Should only ever pass a positive integer to `from_nonnegative_i32`")
}

fn from_negative_i32(index: i32) -> usize {
    static_assertions::const_assert!(usize::BITS >= 32);

    index.checked_neg().map(from_nonnegative_i32).unwrap_or({
        // 'checked_neg' only fails for i32::MIN. We cannot
        // represent -i32::MIN as a i32, but we can represent
        // it as a usize, since usize is at least 32 bits.
        from_nonnegative_i32(i32::MAX) + 1
    })
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
enum Position {
    BeforeStart,
    AtIndex(usize),
    AfterEnd,

View on GitHub (pinned to d1087a4b9e)

Solutions

  1. Reduce to a snippet like `t: tuple[int, str]; t[-1]` and file a ty issue with the backtrace
  2. As a contributor: dispatch on the sign (`if index >= 0 { from_nonnegative_i32 } else { from_negative_i32 }`) before converting
  3. Workaround: rewrite the negative literal subscript as a non-negative index while the bug is unfixed

Example fix

// before
let idx = from_nonnegative_i32(index);

// after
let idx = if index >= 0 {
    from_nonnegative_i32(index)
} else {
    from_negative_i32(index)
};
Defensive patterns

Strategy: validation

Validate before calling

// Caller-side sign dispatch before converting an index:
let idx = if index >= 0 { from_nonnegative_i32(index) } else { from_negative_i32(index) };

Prevention

When it happens

Trigger: Subscript inference (e.g., tuple element binding for `t[-1]` or literal string indexing) passing a negative index into from_nonnegative_i32 instead of from_negative_i32 - typically a missing sign check at a call site or a newly added subscript path that forgot the negative branch.

Common situations: Code that indexes tuples or heterogeneous sequences with negative integer literals; refactors of subscript.rs call sites that drop the sign dispatch.

Related errors


AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20). Data as JSON: /api/errors/64a9ea40ac1f4b60. Report an issue: GitHub.