astral-sh/ruff · error

Expected Stmt::Import | Stmt::ImportFrom

Error message

Expected Stmt::Import | Stmt::ImportFrom

What it means

Internal panic in Ruff's isort rule annotation pass. `annotate_imports` walks statements that the import-block scanner has already classified as import statements, and only matches `Stmt::Import` and `Stmt::ImportFrom`; any other statement reaching it is considered impossible. The panic means the statement filter upstream produced a non-import statement — a Ruff bug or an internal API misuse.

Source

Thrown at crates/ruff_linter/src/rules/isort/annotate.rs:173

                        trailing.push(comment);
                    }

                    AnnotatedImport::ImportFrom {
                        module: module.as_ref().map(|module| locator.slice(module)),
                        names: aliases,
                        level: *level,
                        is_lazy: *is_lazy,
                        trailing_comma: if split_on_trailing_comma {
                            trailing_comma(import, tokens)
                        } else {
                            TrailingComma::default()
                        },
                        atop,
                        inline,
                        trailing,
                    }
                }
                _ => panic!("Expected Stmt::Import | Stmt::ImportFrom"),
            }
        })
        .collect()
}

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Upgrade Ruff to the latest release and re-run; if it persists, minimize the file with `ruff check --isolated --select I001 <file>` and file an issue
  2. Run with `--no-fix` or disable isort (`ignore = ["I001"]`) to continue linting while avoiding the crashing path
  3. If you drive the isort APIs from Rust, ensure the statement list passed to `annotate_imports` contains only `Stmt::Import`/`Stmt::ImportFrom` (filter or match before calling)
  4. Check for plugins/forks that modify the AST before sorting and remove them

Example fix

// before (caller side, Rust)
format_imports(stmts.iter(), ...)

// after
classifier.filter(stmts.iter().filter(|stmt| matches!(stmt, Stmt::Import(_) | Stmt::ImportFrom(_))))
format_imports(filtered, ...)
Defensive patterns

Strategy: validation

Validate before calling

// Rust: filter to import statements before annotating
let imports: Vec<&Stmt> = stmts
    .iter()
    .filter(|s| matches!(s, Stmt::Import(_) | Stmt::ImportFrom(_)))
    .collect();

Type guard

fn is_import_stmt(stmt: &Stmt) -> bool {
    matches!(stmt, Stmt::Import(_) | Stmt::ImportFrom(_))
}

Try / catch

// isort runs in the linter process; guard the whole check invocation
let result = std::panic::catch_unwind(|| ruff_check_isort(path))
    .map_err(|_| "isort annotation panicked");

Prevention

When it happens

Trigger: Calling `format_imports`/`annotate_imports` with a block of statements containing a non-import statement (e.g. because the traversal handed an `Expr` or `Assign` into the import annotator). Not triggerable by normal `ruff check` input unless there is a classification regression in the isort block scanner.

Common situations: Custom distributions of Ruff where a rule or transformer inserts/permutes statements before isort processing; a Ruff upgrade after which import-block boundaries are computed incorrectly; fuzzed/obfuscated Python that breaks block classification.

Related errors


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