astral-sh/ruff · error

Expected at least one import

Error message

Expected at least one import

What it means

Same pattern as the TC004 fix: `fix_imports` (for typing-only runtime imports, TC002) anchors its edits at the earliest reference start of the collected bindings and panics if `imports` is empty. The invariant is that the function only runs when at least one import binding needs moving into a type-checking block.

Source

Thrown at crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs:531

/// Generate a [`Fix`] to remove typing-only imports from a runtime context.
fn fix_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");

    let add_future_import = imports.iter().any(|binding| binding.needs_future_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 a `TYPE_CHECKING` block.
    let (type_checking_edit, add_import_edit) = checker
        .importer()
        .typing_import_edit(
            &ImportedMembers {

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Guard with an early return producing Fix::empty() when imports is empty
  2. Keep the presence check and the fix builder reading the same (unchanged) bindings vector
  3. Handle the Option from min() explicitly instead of expecting

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

if let [first, ..] = imports { /* anchor from first */ }

Prevention

When it happens

Trigger: fix_imports is reached with an empty bindings list — the diagnostic decided a fix exists, but binding collection produced nothing (e.g. every binding lacked a resolvable reference id or was deduplicated away).

Common situations: Contributors encounter this after changing reference resolution or the ImportBinding filter so the presence-check upstream no longer matches the actual list contents.

Related errors


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