astral-sh/ruff · critical

PySourceType always parses into a module

Error message

PySourceType always parses into a module

What it means

Ruff's unchecked parser (`parse_unchecked`) is used for linting when the source type is known; by construction every PySourceType (module, script, notebook, etc.) must yield a parse result that converts into a `Parsed` module. If `try_into_module()` returns None, an internal invariant of the parser/source-type mapping is broken, so the linter panics with this message.

Source

Thrown at crates/ruff_linter/src/linter.rs:806

/// Per-cell modules are merged so definitions remain visible across cells.
pub fn parse_unchecked_source(
    source_kind: &SourceKind,
    source_type: PySourceType,
    target_version: PythonVersion,
) -> Parsed<ModModule> {
    let options = ParseOptions::from(source_type).with_target_version(target_version);
    // SAFETY: Safe because `PySourceType` always parses to a `ModModule`. See
    // `ruff_python_parser::parse_unchecked_source`. We use `parse_unchecked` (and thus
    // have to unwrap) in order to pass the `PythonVersion` via `ParseOptions`.
    match source_kind.as_ipy_notebook() {
        Some(notebook) => ruff_python_parser::parse_cells_unchecked(
            source_kind.source_code(),
            notebook.cell_offsets().content_ranges(),
            &options,
        ),
        None => ruff_python_parser::parse_unchecked(source_kind.source_code(), options)
            .try_into_module()
            .expect("PySourceType always parses into a module"),
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;

    use anyhow::Result;
    use ruff_python_ast::{PySourceType, PythonVersion};
    use ruff_python_codegen::Stylist;
    use ruff_python_index::Indexer;
    use ruff_python_trivia::textwrap::dedent;
    use test_case::test_case;

    use ruff_db::diagnostic::Diagnostic;
    use ruff_notebook::{Notebook, NotebookError};

    use crate::linter::{check_path, parse_unchecked_source};

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Report the crash to the Ruff repository with the input file that triggered it — this is an internal invariant violation, not a user error
  2. Upgrade/downgrade Ruff to a version where the parser regression is fixed
  3. Work around by linting the file as a plain module (avoid the notebook/Percent format detection, e.g. fix the file extension or cell markers)
  4. If developing: verify the source_type and options passed to parse_unchecked match (e.g. don't pass notebook options with SourceType::Module)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure source_kind and options agree before calling the linter
let options = source_type_to_options(source_type.source_type());
assert!(matches!(source_type, PySourceType::Python | PySourceType::Ipynb));

Type guard

fn is_supported_source(kind: &PySourceType) -> bool {
    matches!(kind, PySourceType::Python | PySourceType::Ipynb)
}

Prevention

When it happens

Trigger: Calling `parse_unchecked_source` (directly or via `Linter::run`, `lint_fix`, `into_parsed`, or add-suppressions paths) with a source_type/parser-options combination for which `parse_unchecked` returns tokens that fail `try_into_module` — i.e. only reachable through an internal bug or a mismatched source kind (e.g. feeding IPython-cell options vs a notebook source inconsistently).

Common situations: Encountered by Ruff/ty contributors changing parser APIs or source-kind handling; end users only see it as a crash report when a new parser version regresses notebook or magic-cell handling.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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