astral-sh/ruff · error

Expected a ty `File`, found a ruff `SourceFile`

Error message

Expected a ty `File`, found a ruff `SourceFile`

What it means

ruff_db's Span unifies the two file kinds — UnifiedFile::Ty(File) for ty and UnifiedFile::Ruff(SourceFile) for ruff lint code — so shared diagnostic machinery can render both. expect_ty_file() unwraps the ty variant and panics when the span actually wraps a ruff SourceFile. Hitting it means a ty-only code path (ty rendering or semantic APIs) received a span produced on ruff's side.

Source

Thrown at crates/ruff_db/src/diagnostic/mod.rs:1277

    }

    /// Returns a new `Span` with the given `range` attached to it.
    pub fn with_range(self, range: TextRange) -> Span {
        self.with_optional_range(Some(range))
    }

    /// Returns a new `Span` with the given optional `range` attached to it.
    pub fn with_optional_range(self, range: Option<TextRange>) -> Span {
        Span { range, ..self }
    }

    /// Returns the [`File`] attached to this [`Span`].
    ///
    /// Panics if the file is a [`UnifiedFile::Ruff`] instead of a [`UnifiedFile::Ty`].
    pub fn expect_ty_file(&self) -> File {
        match self.file {
            UnifiedFile::Ty(file) => file,
            UnifiedFile::Ruff(_) => panic!("Expected a ty `File`, found a ruff `SourceFile`"),
        }
    }

    /// Returns the [`SourceFile`] attached to this [`Span`].
    ///
    /// Panics if the file is a [`UnifiedFile::Ty`] instead of a [`UnifiedFile::Ruff`].
    fn expect_ruff_file(&self) -> &SourceFile {
        self.as_ruff_file()
            .expect("Expected a ruff `SourceFile`, found a ty `File`")
    }

    /// Returns the [`SourceFile`] attached to this [`Span`].
    pub fn as_ruff_file(&self) -> Option<&SourceFile> {
        match &self.file {
            UnifiedFile::Ty(_) => None,
            UnifiedFile::Ruff(file) => Some(file),
        }
    }

View on GitHub (pinned to d1087a4b9e)

Solutions

  1. Branch on the variant first: inspect span.file() / as_ruff_file() and only call ty logic for the ty variant, handling the ruff case separately.
  2. If a function genuinely requires a ty File, change its signature to accept File directly so misuse becomes a compile error instead of a runtime panic.
  3. When touching shared rendering code, audit call sites of expect_ty_file() in the same change.

Example fix

// before
let file = span.expect_ty_file(); // panics: Expected a ty `File`, found a ruff `SourceFile`

// after
match span.file() {
    UnifiedFile::Ty(file) => render_ty(file, span.range()),
    UnifiedFile::Ruff(source) => render_ruff(source, span.range()),
}
Defensive patterns

Strategy: type-guard

Validate before calling

debug_assert!(
    matches!(span.file(), UnifiedFile::Ty(_)),
    "ty-only path received a ruff span"
);

Type guard

fn is_ty_span(span: &Span) -> bool {
    matches!(span.file(), UnifiedFile::Ty(_))
}

Prevention

When it happens

Trigger: Calling span.expect_ty_file() (or a helper that does) on a Span built from a ruff SourceFile — typically when shared printer/renderer code in ruff_db is extended and the ruff branch flows into a ty-only function, or ruff lint diagnostics are fed into ty rendering.

Common situations: Refactoring the unified diagnostics layer so a path that previously only saw ty Files now also receives ruff SourceFiles; wiring ruff lint output through the same renderer as ty diagnostics.

Related errors


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