astral-sh/ruff · error
Expected at least one import
Error message
Expected at least one import
What it means
`move_imports` computes the earliest start offset of the imports being moved via `.min()` over reference start positions, expecting the `imports` slice to be non-empty. The panic fires when move_imports is called with zero import bindings, because there is no position to anchor the moved imports.
Source
Thrown at crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs:334
/// Generate a [`Fix`] to remove runtime imports from a type-checking block.
fn move_imports(checker: &Checker, node_id: NodeId, imports: &[ImportBinding]) -> Result<Fix> {
let statement = checker.semantic().statement(node_id);
let parent = checker.semantic().parent_statement(node_id);
let member_names: Vec<Cow<'_, str>> = imports
.iter()
.map(|ImportBinding { import, .. }| import)
.map(Imported::member_name)
.collect();
// Find the first reference across all imports.
let at = imports
.iter()
.map(|ImportBinding { reference_id, .. }| {
checker.semantic().reference(*reference_id).start()
})
.min()
.expect("Expected at least one import");
// Step 1) Remove the import.
let remove_import_edit = fix::edits::remove_unused_imports(
member_names.iter().map(AsRef::as_ref),
statement,
parent,
checker.locator(),
checker.stylist(),
checker.indexer(),
)?;
// Step 2) Add the import to the top-level.
let add_import_edit = checker.importer().runtime_import_edit(
&ImportedMembers {
statement,
names: member_names.iter().map(AsRef::as_ref).collect(),
},
at,View on GitHub (pinned to 26f38c119c)
Solutions
- Early-return (or use Fix::empty()) when `imports.is_empty()` before computing `at`
- Assert/filter at the call site (runtime_import_in_type_checking) that at least one binding exists before building the fix
- Compute min via `iter().map(...).min()` into an Option and handle None explicitly
Example fix
// before
let at = imports.iter().map(...).min().expect("Expected at least one import");
// after
let Some(at) = imports.iter().map(...).min() else { return Fix::empty(); }; Defensive patterns
Strategy: validation
Validate before calling
if imports.is_empty() { return Fix::empty(); } Type guard
fn non_empty<T>(v: &[T]) -> bool { !v.is_empty() } Prevention
- Early-return on empty binding lists before computing anchors
- Verify upstream filters cannot drop all bindings once a diagnostic is emitted
- Handle Option from min() explicitly
When it happens
Trigger: Calling move_imports with an empty `imports` vec — i.e. the runtime-import-in-type-checking rule determined a fix is needed but all ImportBinding entries were filtered out (e.g. bindings without usable reference ids) before the fix step.
Common situations: Hit by contributors when the binding-collection step and the fix step disagree on which imports qualify, typically after adding a new filter to ImportBinding collection without updating the fix entry condition.
Related errors
- Expected at least one import
- Expected at least one reference
- extra use-def data should have been retained
- should be set because `extract_if` only yields elements with
- Should only ever pass a positive integer to `from_nonnegativ
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/5dfae9d4d56615cc.
Report an issue: GitHub.