Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Ruff User Guide

Generated June 2026 from astral-sh/ruff at fd16b26

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Ruff is an extremely fast Python linter and code formatter, written in Rust. This repository is a single Cargo workspace that ships two separate command-line tools:

Tool Binary What it does
Ruff ruff Lints and formats Python source code. A drop-in replacement for many tools (Flake8, isort, pyupgrade, pydocstyle, Black, and more) behind one fast binary.
ty ty An extremely fast Python type checker and language server. (Newer; ships from the same repository.)

This guide is organized by tool. If you only want linting and formatting, read the Ruff sections. If you want type checking, jump to ty. Both tools share the same configuration philosophy (TOML config files, gitignore-aware file discovery, --config overrides), so the concepts transfer between them.

Note

Ruff bundles the behavior of dozens of upstream Python tools. Rather than listing every one of its 800+ lint rules here, this guide shows you how to discover, select, and configure rules using Ruff's own commands (ruff linter, ruff rule, ruff config). Those commands always reflect the exact rules available in your installed version.


Table of Contents


Part 1: Ruff — the linter and formatter

Quick Start

# 1. Install Ruff (see Installation for more methods)
pip install ruff

# 2. Lint the current directory
ruff check

# 3. Lint and automatically fix what can be fixed safely
ruff check --fix

# 4. Format the current directory (Black-compatible style)
ruff format

A typical first run looks like this:

example.py:1:8: F401 [*] `os` imported but unused
example.py:1:1: I001 [*] Import block is un-sorted or un-formatted
Found 2 errors.
[*] 2 fixable with the `--fix` option.

Each diagnostic line is path:line:column: CODE message. A [*] marker means Ruff can fix that violation for you.

Tip

ruff check lints; ruff format formats. They are independent commands — running one never runs the other. A common project setup runs both (ruff check --fix then ruff format).


Installation

Ruff is distributed as a self-contained binary with no Python dependency at runtime.

pip

pip install ruff

uv

# Add Ruff as a development dependency to your project
uv add --dev ruff

# Or run it once without installing
uvx ruff check

pipx

pipx install ruff

From source (Cargo)

# Requires a Rust toolchain
cargo install --git https://github.com/astral-sh/ruff ruff

Verify the installation:

ruff version
ruff 0.15.15

Note

The Python package (pip install ruff) is a thin wrapper that ships the compiled Rust binary for your platform — there is no Python interpreter involved when Ruff runs.


Linting your code (ruff check)

ruff check is the linting command. Point it at files, directories, or - (stdin). With no path, it defaults to the current directory (.).

# Lint the whole project
ruff check

# Lint specific paths
ruff check src/ tests/

# Lint a single file
ruff check app.py

# Lint code piped in on stdin
cat app.py | ruff check -

Frequently used ruff check options

Flag Description
--fix Apply fixes to resolve lint violations. Use --no-fix to disable.
--unsafe-fixes Also apply fixes that may not preserve the original intent. Use --no-unsafe-fixes to disable.
--fix-only Apply fixes but do not report on (or fail for) leftover violations. Implies --fix.
--diff Don't write changes; print a diff of what --fix would do, and exit 0 if there are no diffs.
--show-fixes List every fix that was applied. Use --no-show-fixes to disable.
--watch, -w Re-run automatically whenever files change.
--select <CODES> Comma-separated rule codes to enable (or ALL).
--ignore <CODES> Comma-separated rule codes to disable.
--extend-select <CODES> Enable extra codes on top of the configured selection.
--per-file-ignores <PAIRS> Map file patterns to codes to ignore (e.g. tests/*:S101).
--exclude <PATTERNS> Omit files/directories from analysis.
--extend-exclude <PATTERNS> Add to the configured excludes.
--target-version <VER> Minimum Python version to support (e.g. py39).
--preview Enable unstable rules and fixes. Use --no-preview to disable.
--output-format <FORMAT> Serialization format for violations (default: full).
--output-file <PATH>, -o Write output to a file instead of stdout.
--statistics Print a count for every rule that had at least one violation.
--add-noqa Insert # noqa directives on every failing line instead of reporting.
--ignore-noqa Ignore all existing # noqa comments.
--no-cache, -n Disable cache reads.
--cache-dir <PATH> Use a specific cache directory.
--stdin-filename <PATH> The filename to assume when reading from stdin.
--extension <MAP> Map a file extension to a language, e.g. --extension ipy:ipynb.
--exit-zero Exit 0 even when violations are found.
--exit-non-zero-on-fix Exit non-zero if any file was modified by --fix.
--show-files Print the files Ruff would check with the current settings, then exit.
--show-settings Print the resolved settings Ruff would use for a file, then exit.

Tip

Many boolean flags have a --no- counterpart (e.g. --no-fix, --no-preview, --no-respect-gitignore). This lets a command-line flag override a value set in your configuration file in either direction.

Reading the output

ruff check src/
src/example.py:1:8: F401 [*] `numpy` imported but unused
src/example.py:8:5: F841 [*] Local variable `x` is assigned to but never used
Found 2 errors.
[*] 2 fixable with the `--fix` option.

Use --statistics to get a per-rule summary instead of one line per violation:

ruff check --statistics
12  F401  unused-import
 3  E501  line-too-long

Choosing an output format

Ruff can serialize diagnostics in several formats via --output-format (or the RUFF_OUTPUT_FORMAT environment variable). The default is full, which shows the offending source line with the diagnostic. Other commonly used values include concise (one line per diagnostic), json, and junit.

# Machine-readable JSON, e.g. for a CI dashboard
ruff check --output-format json

# Concise, one diagnostic per line
ruff check --output-format concise

Note

To see every output format your installed version supports, run ruff check --help and look at the --output-format entry.


Fixing violations automatically

Many rules ship an automatic fix. Ruff classifies fixes as safe or unsafe:

  • Safe fixes preserve the meaning of your code and are applied by --fix.
  • Unsafe fixes might change behavior or intent and require --unsafe-fixes to opt in.
# Apply safe fixes
ruff check --fix

# Also apply unsafe fixes
ruff check --fix --unsafe-fixes

# Preview what would change without writing files
ruff check --fix --diff

# Apply fixes and don't fail on remaining violations
ruff check --fix-only

Warning

--fix modifies files in place. Run it on a clean working tree (commit or stash first) so you can review the changes with git diff. Use --diff to preview without writing.

You can constrain which rules are eligible for fixing with --fixable and --unfixable:

# Only let Ruff fix import sorting, even though more rules are enabled
ruff check --fix --fixable I001

Formatting your code (ruff format)

ruff format is Ruff's autoformatter. It produces a consistent, Black-compatible style.

# Format the whole project in place
ruff format

# Format specific paths
ruff format src/ tests/

# Format code from stdin
cat app.py | ruff format -

ruff format options

Flag Description
--check Don't write changes; exit non-zero if any file would be reformatted. Ideal for CI.
--diff Don't write changes; print the diff between current and formatted output.
--line-length <N> Set the maximum line length used for wrapping.
--range <RANGE> Format only a line/column range, e.g. --range=1:1-5:1 (single file only).
--target-version <VER> Minimum Python version to target.
--preview Enable unstable formatting. Use --no-preview to disable.
--exclude <PATTERNS> Omit files/directories from formatting.
--stdin-filename <PATH> Filename to assume when reading from stdin.
--extension <MAP> Map a file extension to a language.
--no-cache, -n Disable cache reads.
--exit-non-zero-on-format Exit non-zero if any file was reformatted.

For CI, the canonical "fail if not formatted" invocation is:

ruff format --check
Would reformat: src/example.py
1 file would be reformatted, 4 files already formatted

Range formatting

--range formats only part of a single file. The format is <start_line>:<start_column>-<end_line>:<end_column>, with 1-based line and column numbers:

# Format lines 10 through 20
ruff format --range=10-20 app.py

# Format from line 2 to the end
ruff format --range=2 app.py

# Format the first three lines
ruff format --range=-3 app.py

Note

Range formatting works on a single file at a time and is not supported for Jupyter notebooks.


Selecting and ignoring rules

Ruff rules are identified by a short code: a linter prefix plus a number. The prefix tells you which upstream tool the rule originates from. A few of the most common families:

Prefix Origin Example
F Pyflakes (logical errors, unused imports/variables) F401 unused import
E, W pycodestyle (style, PEP 8) E501 line too long
I isort (import sorting) I001 unsorted imports
N pep8-naming N802 function name should be lowercase
UP pyupgrade (modern syntax) UP006 use list not List
B flake8-bugbear (likely bugs) B008 function call in default arg
C4 flake8-comprehensions C401 unnecessary generator
SIM flake8-simplify SIM108 use ternary
PL Pylint PLR2004 magic value
RUF Ruff-native rules RUF100 unused noqa

Tip

This is a small sample. Run ruff linter to list every linter family your version supports, and ruff rule <CODE> to read the full explanation of any rule.

Rules can be selected by full code (F401), by prefix (F selects all Pyflakes rules), or with the special selector ALL.

# Enable only Pyflakes and isort
ruff check --select F,I

# Enable everything, then turn off a noisy category
ruff check --select ALL --ignore D

# Add a rule on top of your configured selection
ruff check --extend-select B

The selection flags interact as follows:

  • --select replaces the active rule set.
  • --extend-select adds to the configured set without replacing it.
  • --ignore removes rules from the active set.
  • --per-file-ignores ignores specific rules only in matching files.
# Allow assert statements (S101) only in the tests directory
ruff check --select ALL --per-file-ignores "tests/*:S101"

Suppressing violations with # noqa

To silence a single line, add a # noqa comment. Scope it to specific codes so you don't hide unrelated problems:

import os  # noqa: F401
value = eval(expr)  # noqa: S307
  • # noqa — suppress all violations on this line.
  • # noqa: F401, F841 — suppress only these codes on this line.

Useful related flags:

# Insert `# noqa` on every currently-failing line (great for adopting Ruff gradually)
ruff check --add-noqa

# Optionally add a reason after the codes
ruff check --add-noqa="legacy code, to be cleaned up"

# Ignore all existing noqa comments (see what's really there)
ruff check --ignore-noqa

Tip

Enable the Ruff-native rule RUF100 to flag # noqa comments that are no longer needed, so suppressions don't rot in your codebase.


Discovering rules and settings

Ruff is self-documenting. Use these commands instead of guessing:

# List every linter family Ruff supports
ruff linter

# Explain a specific rule (cause, examples, fix availability)
ruff rule F401

# Explain every rule, as JSON
ruff rule --all --output-format json

# List all configuration options
ruff config

# Show the docs for a single config key
ruff config lint.isort.known-first-party

--show-settings resolves and prints the exact configuration Ruff would apply to a file — indispensable when a rule isn't firing the way you expect:

ruff check --show-settings app.py

--show-files lists which files Ruff would actually analyze under the current settings:

ruff check --show-files

Configuration file

Ruff reads its configuration from (in order of preference) a ruff.toml / .ruff.toml file, or the [tool.ruff] table of a pyproject.toml. Settings are discovered by walking up the directory tree from each file being checked, so sub-projects can have their own configuration.

A representative pyproject.toml:

[tool.ruff]
# Top-level file/format settings
line-length = 88
indent-width = 4
target-version = "py39"
src = ["src", "tests"]
exclude = ["build", "dist", ".venv"]

[tool.ruff.lint]
# Rule selection
select = ["E", "F", "I", "UP", "B"]
ignore = ["E501"]
# Per-file rule ignores
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]
"__init__.py" = ["F401"]

[tool.ruff.lint.isort]
known-first-party = ["my_package"]
lines-after-imports = 2

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

Important

Rule selection (select, ignore, per-file-ignores, etc.) lives under [tool.ruff.lint], not at the top level. Formatting options live under [tool.ruff.format]. The top-level [tool.ruff] table holds settings shared by both, such as line-length, target-version, src, and exclude.

Most-used configuration keys

These are grounded in Ruff's configuration schema. Run ruff config for the complete, version-accurate list.

Top level — [tool.ruff]:

Key Purpose
line-length Maximum line length (also drives E501 and the formatter).
indent-width Number of spaces per indentation level.
target-version Minimum supported Python version (py37…py313).
src Directories treated as first-party source roots (affects import sorting).
include / exclude File patterns to analyze / skip.
extend-include / extend-exclude Add to the include/exclude sets.
respect-gitignore Honor .gitignore and similar files (default: true).
force-exclude Apply excludes even to paths passed explicitly on the CLI.
fix / fix-only / unsafe-fixes Default fix behavior.
output-format Default diagnostic format.
preview Enable unstable rules/features.
required-version Pin the Ruff version a project expects.
extend Inherit settings from another config file.
cache-dir Where to store the lint cache.

Linting — [tool.ruff.lint]:

Key Purpose
select / ignore Rules to enable / disable.
extend-select / extend-fixable / extend-unfixable Additive rule selection.
per-file-ignores / extend-per-file-ignores Per-pattern rule ignores.
fixable / unfixable Restrict which rules may be auto-fixed.
dummy-variable-rgx Regex matching intentionally-unused variable names.
task-tags Tags recognized in comments (e.g. TODO, FIXME).
typing-modules Modules treated as providing typing constructs.
external Codes from other tools that Ruff should not flag as unknown in noqa.
allowed-confusables Unicode confusable characters to permit.

Each upstream plugin also has its own sub-table — for example [tool.ruff.lint.isort], [tool.ruff.lint.pycodestyle], [tool.ruff.lint.pydocstyle], [tool.ruff.lint.mccabe], [tool.ruff.lint.pylint], [tool.ruff.lint.flake8-bugbear], [tool.ruff.lint.flake8-quotes], and many more. Run ruff config lint.<plugin> to see the keys for each.

Formatting — [tool.ruff.format]:

Key Purpose
quote-style Preferred string quotes (e.g. double, single).
indent-style Indent with space or tab.
line-ending Line ending style (e.g. lf, crlf, auto).
skip-magic-trailing-comma Ignore the "magic" trailing comma when collapsing collections.
docstring-code-format Format code examples inside docstrings.
docstring-code-line-length Line length for code inside docstrings.
preview Enable unstable formatting.

Overriding config on the command line

The global --config flag accepts either a path to a config file or an inline KEY = VALUE TOML override. Inline overrides take precedence over every config file.

# Use a specific config file
ruff check --config path/to/ruff.toml

# Override a single setting inline
ruff check --config "lint.line-length = 100"
ruff check --config "format.quote-style = 'single'"

# Combine: a file plus targeted overrides
ruff check --config ruff.toml --config "extend-select = ['I']"

To ignore all configuration files entirely, use --isolated:

ruff check --isolated --select F

Caution

--isolated cannot be combined with a --config file (they conflict), but it can be combined with inline --config "KEY = VALUE" overrides. Mixing --isolated with a config file path produces an error.


Other ruff commands

Command Purpose
ruff check [PATHS] Lint files or directories.
ruff format [PATHS] Format files or directories.
ruff rule <CODE> / ruff rule --all Explain a rule (or all rules).
ruff linter List all supported upstream linters.
ruff config [KEY] List or describe configuration options.
ruff clean Clear Ruff's caches in the current directory and subdirectories.
ruff analyze graph [PATHS] Generate a map of Python file dependencies (see below).
ruff server Run the Ruff language server (for editors).
ruff version Print Ruff's version.
ruff help <command> Show help for any subcommand.

Import dependency graphs (ruff analyze graph)

ruff analyze graph produces a JSON map of which files import which:

# Map each file to the files it depends on
ruff analyze graph src/

# Map each file to the files that depend on it
ruff analyze graph --direction dependents src/

# Try to resolve imports written as string literals
ruff analyze graph --detect-string-imports src/

Global options (available on every command)

Flag Description
--config <FILE-or-KEY=VALUE> Config file path or inline TOML override.
--isolated Ignore all configuration files.
--color <WHEN> auto, always, or never.
--verbose, -v Verbose logging.
--quiet, -q Print diagnostics only.
--silent, -s Suppress all logging (still exits non-zero on diagnostics).

Editor integration (ruff server)

Ruff includes a built-in language server, started with ruff server. Most editors invoke it for you through an extension rather than you running it directly. It provides real-time diagnostics, quick-fixes, import organization, and formatting using the same engine as the CLI, so editor results match what ruff check and ruff format produce.

# Run the language server (normally launched by your editor)
ruff server

# Enable unstable server features
ruff server --preview

Tip

Because the server shares Ruff's configuration resolution, the rules and formatting you see in your editor are exactly what CI will enforce — as long as both use the same pyproject.toml/ruff.toml.


Part 2: ty — the type checker

ty is an extremely fast Python type checker that ships from the same repository as Ruff. It analyzes your code for type errors and can also run as a language server.

Note

ty is a separate binary from ruff. Installing one does not install the other unless your packaging method bundles both. Where ruff check looks for lint violations, ty check looks for type errors.

Quick Start (ty)

# Check the current project for type errors
ty check

# Check specific paths
ty check src/

# Print ty's version
ty version

A successful run reports:

All checks passed!

When ty finds problems, it summarizes them:

Found 3 diagnostics

Checking a project (ty check)

ty check analyzes a project for type errors. With no path, it checks the project root. Project metadata (pyproject.toml) is discovered by walking up the directory tree.

# Check the whole project
ty check

# Check specific files or directories
ty check src/ app.py

# Run from a specific project directory
ty check --project ./backend

Frequently used ty check options

Flag Description
--project <PROJECT> Run within the given project directory.
--python <PATH> Path to your project's Python environment or interpreter (alias: --venv).
--python-version <VERSION> Python version to assume when resolving types (alias: --target-version).
--python-platform <PLATFORM> Target platform for sys.platform specialization (alias: --platform).
--extra-search-path <PATH> Additional module-resolution search path (repeatable).
--typeshed <PATH> Custom directory for stdlib typeshed stubs (alias: --custom-typeshed-dir).
--fix Apply fixes to resolve errors.
--add-ignore Insert ty: ignore comments to suppress all rule diagnostics.
--error <RULE> / --warn <RULE> / --ignore <RULE> Set a rule's severity (repeatable; all targets every rule).
--config <KEY=VALUE>, -c Inline TOML configuration override.
--config-file <PATH> Path to a ty.toml file.
--output-format <FORMAT> Diagnostic format: full, concise, gitlab, github, junit.
--error-on-warning Exit 1 if there are any warning-level diagnostics.
--exit-zero Always exit 0, even when there are errors.
--watch, -W Watch files and recheck on change.
--exclude <GLOB> Glob patterns for files to exclude (gitignore-style).
--respect-ignore-files Honor .gitignore etc. (use --no-respect-ignore-files to disable).
--force-exclude Apply excludes even to paths passed directly on the CLI.
--color <WHEN> auto, always, or never.
--no-progress Hide spinners and progress bars.
--verbose, -v Verbose output (-vv, -vvv for more).
--quiet, -q Quiet output (-qq for silent).

Watch mode

ty check --watch

ty stays running, re-checking files related to whatever you change. Press Ctrl+C to stop.


Pointing ty at your environment

ty resolves third-party imports using your project's Python environment. If you use a project manager like uv, or have an activated virtual environment / Conda environment, ty usually finds it automatically. When it can't, point it explicitly:

# Point at a virtual environment directory
ty check --python .venv

# Point at a specific interpreter
ty check --python .venv/bin/python3

# Point at a system Python prefix
ty check --python /usr

The Python version ty assumes affects which syntax is allowed and which standard-library and conditional type definitions apply. ty determines it in this order:

  1. project.requires-python in pyproject.toml (minimum of the range), then
  2. the Python version inferred from your configured/activated environment, then
  3. the latest stable Python version ty supports.

Override it directly when needed:

ty check --python-version 3.12

Selecting rule severities

Unlike Ruff's enable/disable model, ty rules are tuned by severity. Use --error, --warn, and --ignore, each repeatable, with later flags overriding earlier ones. Use all to target every rule at once.

# Treat a specific rule as an error
ty check --error possibly-unresolved-reference

# Downgrade a rule to a warning
ty check --warn division-by-zero

# Silence a rule entirely
ty check --ignore unused-ignore-comment

# Make everything an error, then relax one rule
ty check --error all --warn redundant-cast

To suppress diagnostics inline in your source, use a ty: ignore comment, or let ty add them for you across the project:

ty check --add-ignore
Added 7 ignore comments

Explain any rule with ty explain:

ty explain rule division-by-zero

Configuring ty

ty reads configuration from a ty.toml file, or from the [tool.ty] table of a pyproject.toml. (When using --config-file, only a standalone ty.toml is accepted.)

[tool.ty.src]
exclude = ["./vendor", "**/__pycache__/**"]

[tool.ty.environment]
python-version = "3.12"

[tool.ty.rules]
possibly-unresolved-reference = "error"
division-by-zero = "error"
unused-ignore-comment = "error"

Override any setting inline with -c / --config:

ty check -c "environment.python-version = '3.13'"
ty check -c "rules.division-by-zero = 'warn'"

Set a config file path with --config-file or the TY_CONFIG_FILE environment variable:

ty check --config-file ./config/ty.toml

Other ty commands

Command Purpose
ty check [PATHS] Check a project for type errors.
ty server Start the ty language server.
ty explain rule <RULE> Explain a rule (or all rules if omitted).
ty version Print ty's version.

Common Workflows

Lint + format + type-check in one local pass

ruff check --fix      # fix lint violations
ruff format           # apply formatting
ty check              # check for type errors

Continuous integration (no writes, fail on any issue)

ruff check --output-format github        # annotate violations in GitHub Actions
ruff format --check                       # fail if anything is unformatted
ty check --error-on-warning               # fail on type errors and warnings

Tip

In CI, ruff format --check and ruff check (without --fix) never modify files — they only report and set the exit code. That makes them safe to run on pull requests.

Adopting Ruff in an existing, messy codebase

# 1. Start strict, then suppress everything currently failing
ruff check --select ALL --add-noqa

# 2. Commit the noqa baseline, then chip away at categories over time
ruff check --select ALL --ignore D,ANN   # e.g. defer docstrings & annotations

# 3. Later, find suppressions you no longer need
ruff check --select RUF100

Pre-commit-style local gate

ruff check --fix && ruff format && ty check

This fixes and formats, then type-checks — and stops at the first command that exits non-zero.


Environment Variables

Variable Tool Effect
RUFF_OUTPUT_FORMAT ruff Default value for --output-format.
RUFF_OUTPUT_FILE ruff Default value for --output-file.
RUFF_NO_CACHE ruff Disable cache reads (same as --no-cache).
RUFF_CACHE_DIR ruff Cache directory location.
TY_CONFIG_FILE ty Path to a ty.toml configuration file.
TY_OUTPUT_FORMAT ty Default value for --output-format.
TY_LOG ty Tracing filter directives (overrides -v/-q verbosity).
TY_LOG_PROFILE ty Set to 1/true to write a flamegraph profile (tracing.folded).
TY_MEMORY_REPORT ty Print a memory report after a check: short, full, or json.
TY_MAX_PARALLELISM ty Cap the number of worker threads ty uses.

Note

A --prefixed CLI flag always takes precedence over the matching environment variable, which in turn takes precedence over a configuration file.


Exit Codes

Ruff (ruff check / ruff format):

Code Meaning
0 No violations found (or none remaining after --fix).
1 Violations were found. Use --exit-zero to force 0, or --exit-non-zero-on-fix to fail when files were modified.

ty (ty check):

Code Meaning
0 Checking succeeded with no errors. (Warnings also exit 0 unless --error-on-warning.)
1 Type errors were found (or warnings, with --error-on-warning). Use --exit-zero to force 0.
2 Invocation error (e.g. bad arguments, missing working directory).
101 Internal ty error (a panic or other unexpected failure).

Troubleshooting

Error: No Python files found under the given path(s)

Cause: The path you passed contains no .py files after exclusions are applied, or the files are excluded by your configuration (or .gitignore).

Solution: Confirm the path is correct, and check whether --force-exclude or an exclude pattern is filtering everything out. Use ruff check --show-files to see exactly which files Ruff would analyze.

Prevention: Keep exclude/extend-exclude patterns narrow, and remember that passing a file explicitly does not bypass excludes when force-exclude is enabled.

Error: You cannot specify more than one configuration file on the command line.

Cause: Two --config flags both pointed at config files (as opposed to inline KEY = VALUE overrides).

Solution: Pass at most one config file path. Use additional --config "KEY = VALUE" flags for any further overrides.

Error: The argument --config=... cannot be used with --isolated

Cause: --isolated ignores all configuration files, so naming a config file contradicts it.

Solution: Drop either --isolated or the --config file. You may still use --isolated together with inline --config "KEY = VALUE" overrides.

A --config "KEY = VALUE" override is rejected as invalid

Cause: The value isn't valid TOML, or it isn't a real Ruff option, or you tried to set extend via --config (which is not allowed because extend decides which files are read in the first place).

Solution: Quote string values as TOML (--config "format.quote-style = 'single'"), verify the key with ruff config, and set extend only inside a config file. Ruff's error output suggests the valid sub-keys when you target a table.

Rules I configured aren't firing (or unexpected ones are)

Cause: Configuration is resolved per-file by walking up the directory tree, so a nested pyproject.toml/ruff.toml may be overriding your top-level settings.

Solution: Run ruff check --show-settings <file> to print the exact, resolved settings for that file, including which rules are active.

ruff format and the editor disagree, or formatting "flip-flops"

Cause: The CLI and the editor are using different configurations or different Ruff versions.

Solution: Ensure both use the same pyproject.toml/ruff.toml, and pin the version with required-version in [tool.ruff]. ruff server shares CLI config, so aligning the config aligns the results.

ty: ty failed followed by a Cause: chain

Cause: ty hard-errored before type checking — typically a configuration resolution or I/O problem (this is distinct from a type error). The exit code is 2.

Solution: Read the Cause: lines; they chain from the high-level failure to the root (e.g. a malformed pyproject.toml). Fix the configuration and re-run.

ty can't resolve third-party imports

Cause: ty cannot find the Python environment where your dependencies are installed.

Solution: Pass --python pointing at your virtual environment, interpreter, or system prefix (e.g. ty check --python .venv). For unconventional layouts, add module roots with --extra-search-path.

ty reports the wrong Python version's behavior

Cause: ty inferred a different python-version than you intended.

Solution: Set python-version explicitly via --python-version, the [tool.ty.environment] table, or project.requires-python in pyproject.toml.


FAQ

Q: Is Ruff a replacement for Flake8, isort, and Black? Ruff reimplements the rules of many tools (Pyflakes, pycodestyle, isort, pyupgrade, pydocstyle, flake8 plugins, and more) as a single fast binary, plus a Black-compatible formatter. Run ruff linter to see every upstream linter it covers.

Q: What's the difference between ruff check and ruff format? ruff check finds (and can fix) lint violations. ruff format rewrites your code to a consistent style. They are independent commands; most projects run both.

Q: What's the difference between Ruff and ty? Ruff lints and formats. ty type-checks. They are separate binaries built from the same repository and share configuration conventions, but they solve different problems.

Q: How do I see all available rules? Run ruff linter for linter families and ruff rule <CODE> (or ruff rule --all) for individual rule explanations. These reflect exactly what your installed version supports.

Q: What's the difference between safe and unsafe fixes? Safe fixes preserve your code's meaning and are applied by --fix. Unsafe fixes might change behavior and require --unsafe-fixes. Preview any fix with ruff check --fix --diff.

Q: How do I suppress a single warning? Add # noqa: <CODE> to the line for Ruff, or # ty: ignore for ty. To bulk-add, use ruff check --add-noqa or ty check --add-ignore.

Q: Where does Ruff store its cache and how do I clear it? Ruff caches results in its cache directory (configurable via cache-dir or RUFF_CACHE_DIR). Clear it with ruff clean, or bypass it with --no-cache.

Q: How do I run Ruff/ty in CI without modifying files? Use ruff check (without --fix), ruff format --check, and ty check. None of these write to your files; they only report and set the exit code.

Q: Can I configure Ruff per-directory? Yes. Configuration is resolved by walking up from each file, so nested ruff.toml / pyproject.toml files apply to their subtrees. Use extend to inherit a base config, and --show-settings to verify what resolves for a given file.

Q: How do I target a specific Python version? For Ruff, set target-version (in [tool.ruff]) or pass --target-version. For ty, set python-version or pass --python-version.