astral-sh/ruff · error

should be set because `extract_if` only yields elements with

Error message

should be set because `extract_if` only yields elements with a primary_span

What it means

Diagnostics are partitioned with extract_if keeping only those whose primary_span() is Some, and inside the loop the span is fetched again with expect. This is an internal consistency check that Diagnostic::primary_span() is a pure, stable accessor: it must return the same Some on the second call as it did in the filter predicate.

Source

Thrown at crates/ty_python_semantic/src/fixes.rs:113

    let has_fixable = diagnostics
        .iter()
        .any(|diagnostic| fix_mode.is_fixable(diagnostic));

    // Early return if there are no diagnostics that can be suppressed to avoid all the heavy work below.
    if !has_fixable {
        return Ok(FixAllResults {
            diagnostics,
            count: 0,
        });
    }

    let mut by_file: BTreeMap<File, Vec<_>> = BTreeMap::new();

    // Group the diagnostics by file, leave the file-agnostic diagnostics in `diagnostics`.
    for diagnostic in diagnostics.extract_if(.., |diagnostic| diagnostic.primary_span().is_some()) {
        let span = diagnostic
            .primary_span()
            .expect("should be set because `extract_if` only yields elements with a primary_span");

        by_file
            .entry(span.expect_ty_file())
            .or_default()
            .push(diagnostic);
    }

    // Identify all files with fixes and queue them for fixing.
    let mut queue: Vec<(QueuedFile, Vec<ApplicableFix>)> = Vec::new();
    let mut source_texts = SourceTexts::default();

    for (&file, diagnostics) in &by_file {
        let path = file.path(db);
        let Some(path) = path.as_system_path() else {
            tracing::debug!("Skipping read-only file `{path}`");
            continue;
        };

View on GitHub (pinned to d1087a4b9e)

Solutions

  1. Make primary_span() a pure accessor over data stored at Diagnostic construction time
  2. Audit the diagnostic type named in the panic backtrace for interior mutability or Option-consuming logic inside the span accessor
  3. File a ty issue with the diagnostic name from the stack trace if the culprit is not obvious
Defensive patterns

Strategy: fallback

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| fix_all(&mut db, diagnostics, fix_mode, &token, check)));
match result {
    Ok(r) => r?,
    Err(_) => { log::warn!("fix-all panicked; falling back to check-only"); FixAllResults { diagnostics: leftover, count: 0 } }
}

Prevention

When it happens

Trigger: A Diagnostic type whose primary_span() is not idempotent: it returns Some during the filter call but None when re-queried, e.g., an accessor that takes out an internal Option, derives the span from mutable state, or is affected by mutation between the two calls.

Common situations: Newly added diagnostic kinds that do not store their primary span at construction; refactors that make span computation lazy or stateful.

Related errors


AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20). Data as JSON: /api/errors/aead1702fe4ac472. Report an issue: GitHub.