Skip to content

Ruff Rule Codes Explained: What E, W, F, I, UP, S, B, and C90 Mean

Run ruff check on a codebase and it answers in codes: F401, E711, B006. The letters are not severity levels, and they are not arbitrary. Each prefix names the linter the rule originally came from.

Ruff re-implements 968 rules (as of version 0.15), most drawn from older linters in the flake8 ecosystem, and it kept the established codes wherever it could. Learn a handful of prefixes and the wall of codes turns into a map of the tools Ruff replaced.

How to read a rule code

A rule code is a prefix plus a number. The prefix identifies the source linter; the number identifies the rule within it:

F401
│└─ rule 401 in that linter's catalog
└── F = Pyflakes

Prefixes run from one to five characters (F, UP, C90, ASYNC), and configuration selects rules by prefix. In pyproject.toml, select = ["E"] enables every pycodestyle error, ["E7"] enables only the statement-level checks like E711, and ["E711"] enables that one rule:

[tool.ruff.lint]
extend-select = [
    "I",   # all isort rules
    "B",   # all flake8-bugbear rules
    "S1",  # only the S1xx flake8-bandit rules
]

Match each prefix to its source tool

The prefixes from this page’s title cover the categories new Ruff users meet first:

Prefix Source tool What it catches
E, W pycodestyle PEP 8 style: indentation, whitespace, comparisons (E errors, W warnings)
F Pyflakes Logical errors: unused imports, undefined names, duplicate arguments
I isort Import sorting and grouping
UP pyupgrade Syntax made obsolete by your target Python version
S flake8-bandit Security problems: hardcoded passwords, pickle, weak hashes
B flake8-bugbear Likely bugs: mutable default arguments, useless expressions
C90 mccabe Cyclomatic complexity over a threshold (one rule, C901)

These eight prefixes are a fraction of the catalog. Ruff 0.15 organizes its rules into 59 groups, including SIM (flake8-simplify) and PL (Pylint). Run ruff linter to print the full prefix-to-tool list for your installed version.

Why does Ruff use other tools’ codes?

The prefixes predate Ruff. They are inherited from the flake8 plugin ecosystem, where each tool claimed its own letter namespace:

  • pycodestyle defined the E and W codes. It was originally released as pep8 and renamed in 2016 at Guido van Rossum’s request, to stop people from conflating the tool with the PEP 8 document itself (pycodestyle issue #466).
  • Pyflakes never had codes of its own. flake8 assigned the F prefix to Pyflakes messages when it wrapped the tool (flake8 error codes).
  • Plugins picked letters on a first-come basis. flake8-bugbear took B, so when flake8-bandit ported bandit’s checks (whose native IDs also start with B), it shifted them to S for security.
  • The letter C is shared by two plugins: mccabe uses C9xx and flake8-comprehensions uses C4xx. Ruff lists them under separate selectors, C90 and C4, so each can be enabled on its own (a bare C selects both).

Ruff kept these codes for the major categories, so a flake8 user’s select and ignore lists and # noqa: F401 comments mostly carry over. A few plugins were re-prefixed along the way: flake8-commas’ C812 became COM812, and Pylint’s codes gained a PL prefix. Where a source tool never had codes (isort and pyupgrade rewrite files rather than report numbered violations), Ruff coined the prefixes itself: I holds two isort rules, UP holds 48 pyupgrade rules. Rules original to Ruff live under RUF.

How do you look up a rule?

Pass any code to ruff rule and it prints the rule’s documentation in the terminal:

$ ruff rule F401
# unused-import (F401)

Derived from the **Pyflakes** linter.

Fix is sometimes available.

## What it does
Checks for unused imports.
...

The same content is in the Ruff rules index, which lists all 968 rules grouped by source tool.

Which categories should you enable?

Out of the box, Ruff enables only the Pyflakes rules (F) and a subset of pycodestyle errors (E4, E7, E9). That default catches real bugs like unused imports and comparisons to None, but no style, import-order, or security rules until you opt in.

For a curated starting set, see How to configure recommended Ruff defaults. Two categories from the table have dedicated guides: How to sort Python imports with Ruff covers I, and How to enable Ruff’s security rules covers S.

Learn More

Last updated on