astral-sh/ruff · error

`isinstance` should have two arguments

Error message

`isinstance` should have two arguments

What it means

The duplicate-isinstance rule assumes every collected index points to an `isinstance` call whose first positional argument (the object being tested) exists. `args.first().expect("`isinstance` should have two arguments")` panics if the call has no positional arguments. Real `isinstance(x, T)` calls always have two args, so this is a defensive invariant.

Source

Thrown at crates/ruff_linter/src/rules/flake8_simplify/rules/ast_bool_op.rs:382

                .push(index);
        } else {
            last_target_option = Some(target.into());
            duplicates.push(vec![index]);
        }
    }

    // Generate a `Diagnostic` for each duplicate.
    for indices in duplicates {
        if indices.len() > 1 {
            // Grab the target used in each duplicate `isinstance` call (e.g., `obj` in
            // `isinstance(obj, int)`).
            let target = if let Expr::Call(ast::ExprCall {
                arguments: Arguments { args, .. },
                ..
            }) = &values[indices[0]]
            {
                args.first()
                    .expect("`isinstance` should have two arguments")
            } else {
                unreachable!("Indices should only contain `isinstance` calls")
            };
            let mut diagnostic = checker.report_diagnostic(
                DuplicateIsinstanceCall {
                    name: if let Expr::Name(ast::ExprName { id, .. }) = target {
                        Some(id.to_string())
                    } else {
                        None
                    },
                },
                expr.range(),
            );
            if !contains_effect(target, |id| checker.semantic().has_builtin_binding(id)) {
                // Flatten the type expressions from each duplicate `isinstance` call into the
                // elements they would contribute to the merged tuple. Tuple operands splice
                // their elements; everything else contributes itself.
                let flattened: Vec<&Expr> = indices

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Filter collected indices to calls with `args.len() >= 2` instead of relying on the expect
  2. Replace the expect with `args.first()?` and skip the diagnostic when the arg is missing
  3. Confirm the index-collection predicate still matches exactly `isinstance` calls with two arguments

Example fix

// before
let target = args.first().expect("`isinstance` should have two arguments");
// after
let Some(target) = args.first() else { continue; };
Defensive patterns

Strategy: type-guard

Validate before calling

// Only collect indices of calls with a first positional arg
if call.arguments.args.first().is_some() { indices.push(i); }

Type guard

fn is_two_arg_isinstance(expr: &Expr) -> bool {
    matches!(expr, Expr::Call(c) if c.arguments.args.len() >= 2)
}

Prevention

When it happens

Trigger: Reachable only through AST states that don't occur in valid Python: an `isinstance` call with zero positional arguments (e.g. `isinstance()`), which is a syntax/parse-level error normally rejected earlier, or a mis-tagged index in `values`/`indices`.

Common situations: Contributors hit this when the loop collects indices for call expressions that are not actually `isinstance` calls (e.g. after a refactor of the filter predicate), or when testing against error-tolerant parses of invalid code.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05). Data as JSON: /api/errors/ee0c4c2a8ca43af5. Report an issue: GitHub.