astral-sh/ruff · error
Expected at least one reference
Error message
Expected at least one reference
What it means
When generating the fix that quotes runtime imports inside a `if TYPE_CHECKING:` block, the rule collects quote_reference_edits and expects at least one. `rest.next().expect("Expected at least one reference")` panics on an empty list, because Fix::unsafe_edits needs a head edit. The invariant is that the function is only called when there is at least one reference to quote.
Source
Thrown at crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs:310
let reference = checker.semantic().reference(*reference_id);
if reference.in_runtime_context() {
Some(quote_annotation(
reference.expression_id()?,
checker.semantic(),
checker.stylist(),
checker.locator(),
checker.default_string_flags(),
))
} else {
None
}
})
})
.collect::<Vec<_>>(),
);
let mut rest = quote_reference_edits.into_iter();
let head = rest.next().expect("Expected at least one reference");
Fix::unsafe_edits(head, rest).isolate(Checker::isolation(
checker.semantic().parent_statement_id(node_id),
))
}
/// 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 = importsView on GitHub (pinned to 26f38c119c)
Solutions
- Guard the call site: return early (or use Fix::empty()) when quote_reference_edits is empty
- Ensure the function is only called after verifying at least one binding reference exists
- Replace the iterator/expect with a Vec and match on `first`/`split_first`
Example fix
// before
let head = rest.next().expect("Expected at least one reference");
Fix::unsafe_edits(head, rest)
// after
let Some(head) = rest.next() else { return Fix::empty(); };
Fix::unsafe_edits(head, rest) Defensive patterns
Strategy: validation
Validate before calling
if quote_reference_edits.is_empty() { return Fix::empty(); } Type guard
if let [head, tail @ ..] = quote_reference_edits.as_slice() { /* build fix */ } Prevention
- Check list emptiness before constructing multi-edit fixes
- Keep the 'has references' check and fix builder adjacent
- Use slice patterns instead of iterator expect
When it happens
Trigger: quote_imports is invoked for an import whose bindings produced zero quote edits — e.g. all references were filtered out (unresolvable members, references outside the type-checking block, or deduplication removing everything) before the fix is built.
Common situations: Contributors hit this after changing the reference-collection or filtering logic upstream so that an import with no quotable references still reaches the fix-building path.
Related errors
- Expression not found
- Expected at least one import
- Expected at least one import
- extra use-def data should have been retained
- should be set because `extract_if` only yields elements with
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/19cdb2eb44fa5f5a.
Report an issue: GitHub.