How to Fix and Ignore Ruff Linter Errors in Python Code
I ran ruff check on my Python project and got hit with a wall of errors. Here’s what I saw:
$ uv run ruff checksrc/numbers/calculate.py:3:8: F401 [*] `os` imported but unusedsrc/numbers/calculate.py:7:15: E501 line too long (92 > 88)src/utils/helpers.py:12:1: F401 [*] `json` imported but unusedsrc/utils/helpers.py:45:80: E501 line too long (95 > 88)Found 4 errors.[*] 2 fixable with the `--fix` option.I stared at the output. Some errors had that [*] marker, some didn’t. What did that mean? And how was I supposed to deal with all of these?
The Two Types of Errors
The [*] marker is crucial—it tells you Ruff can automatically fix that error. Let me show you the difference:
F401 [*] `os` imported but unused <-- Auto-fixable (remove unused import)E501 line too long (92 > 88) <-- NOT auto-fixable (requires manual edit)For auto-fixable errors, Ruff knows exactly what to do. For others, you need to make the call.
My First Mistake: Manually Deleting Imports
I initially opened the file and deleted the unused import myself:
from typing import Iterableimport os # I deleted this line manually
def sum_even_numbers(numbers: Iterable[int]) -> int: ...This worked, but it was tedious. I had 12 files with similar issues. Then I re-read the Ruff output more carefully:
[*] 2 fixable with the `--fix` option.The --fix Flag
Ruff can fix many errors automatically:
$ uv run ruff check --fixFound 4 errors (2 fixed, 2 remaining).
src/numbers/calculate.py:7:15: E501 line too long (92 > 88)src/utils/helpers.py:45:80: E501 line too long (95 > 88)The two unused imports were removed automatically. The line-too-long errors remain because Ruff can’t know how you want to restructure the code.
Here’s what happened to my file:
from typing import Iterable
def sum_even_numbers(numbers: Iterable[int]) -> int: ...The import os line is gone. No manual editing required.
When You Can’t Auto-Fix: The noqa Approach
Some errors require your judgment. Take this line:
result = some_really_long_function_name(first_argument, second_argument, third_argument)If the line is genuinely long, you have three options:
Option 1: Refactor the code (preferred)
result = some_really_long_function_name( first_argument, second_argument, third_argument)Option 2: Inline suppression for specific rule
result = some_really_long_function_name(first_argument, second_argument, third_argument) # noqa: E501Option 3: File-wide suppression
# ruff: noqa: E501
def sum_even_numbers(numbers: Iterable[int]) -> int: result = some_really_long_function_name(first_argument, second_argument, third_argument) ...The key difference: # noqa: E501 suppresses for one line, # ruff: noqa: E501 suppresses for the entire file.
My Second Mistake: Using noqa Without a Code
I initially wrote:
import os # noqaThis suppresses ALL rules for that line, not just the unused import. The correct approach:
import os # noqa: F401Always specify the error code. This way, if you later add code that uses os, Ruff will warn you that the suppression is no longer needed.
The --add-noqa Flag: Adding Suppressions Automatically
When you enable a new rule across an existing codebase, you might get dozens of violations. Instead of manually adding # noqa comments, Ruff can do it for you:
$ uv run ruff check --select UP035 --add-noqa .Added 3 noqa directives.This scans your files and adds # noqa: UP035 comments to lines that violate the new rule. It’s useful for gradual adoption of stricter linting.
Here’s what it added:
from typing import Iterable # noqa: UP035
def sum_even_numbers(numbers: Iterable[int]) -> int: ...The UP035 rule warns about deprecated import forms. Adding the suppression lets you continue working while you plan the migration.
Common Ruff Error Codes
Here are the codes I encounter most often:
| Code | Description | Auto-fixable? |
|---|---|---|
| F401 | Imported but unused | Yes |
| F841 | Local variable assigned but never used | Yes |
| E501 | Line too long | No |
| E712 | Comparison to True should be is True | Yes |
| UP035 | Deprecated import form | No |
| UP007 | Use `X | Y` for type annotations |
| I001 | Import block is un-sorted | Yes |
Which Approach to Use When
Error appears | vIs it auto-fixable? (check for [*]) | +--+--+ | | Yes No | | v v --fix Is this a legitimate violation? | +--+--+ | | Yes No (it's intentional) | | v v Fix the Add noqa comment codeUse --fix when: The error is auto-fixable and you agree with the fix.
Use # noqa: CODE when: The violation is intentional or you have a good reason to keep it.
Use # ruff: noqa: CODE when: A specific rule doesn’t apply to the entire file (e.g., line length in generated code).
Use --add-noqa when: You’re adopting a new rule and want to suppress existing violations while enforcing it on new code.
My Workflow Now
- Run
ruff checkto see all errors - Run
ruff check --fixto auto-fix what’s possible - Review remaining errors and decide: fix or suppress
- For intentional violations, add specific
# noqa: CODEcomments - For new rules on existing code, use
--add-noqafor gradual adoption
This workflow keeps my code clean while giving me control over intentional choices.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments