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
- Part 2: ty — the type checker
- Common Workflows
- Environment Variables
- Exit Codes
- Troubleshooting
- FAQ
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 checklints;ruff formatformats. They are independent commands — running one never runs the other. A common project setup runs both (ruff check --fixthenruff 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 --helpand look at the--output-formatentry.
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-fixesto 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
--fixmodifies files in place. Run it on a clean working tree (commit or stash first) so you can review the changes withgit diff. Use--diffto 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 linterto list every linter family your version supports, andruff 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:
--selectreplaces the active rule set.--extend-selectadds to the configured set without replacing it.--ignoreremoves rules from the active set.--per-file-ignoresignores 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
RUF100to flag# noqacomments 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 asline-length,target-version,src, andexclude.
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
--isolatedcannot be combined with a--configfile (they conflict), but it can be combined with inline--config "KEY = VALUE"overrides. Mixing--isolatedwith 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
tyis a separate binary fromruff. Installing one does not install the other unless your packaging method bundles both. Whereruff checklooks for lint violations,ty checklooks 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:
project.requires-pythoninpyproject.toml(minimum of the range), then- the Python version inferred from your configured/activated environment, then
- 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 --checkandruff 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.