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> = indicesView on GitHub (pinned to 15f3fe6b15)
Solutions
- Filter collected indices to calls with `args.len() >= 2` instead of relying on the expect
- Replace the expect with `args.first()?` and skip the diagnostic when the arg is missing
- 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
- Filter indices by argument count at collection time
- Never index into args without a first()/get() guard
- Add mdtests for malformed isinstance calls
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
- last_target should have a corresponding entry
- extra use-def data should have been retained
- expected class
- expected function
- expected type alias
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05).
Data as JSON: /api/errors/ee0c4c2a8ca43af5.
Report an issue: GitHub.