Skip to content
Latchkey

ruff / flake8 Lint Failures Breaking CI - Fix or Configure

A linter exited non-zero because it found violations. Often they are real and should be fixed; sometimes CI fails where local passes because the linter version or config differs between environments.

What this error means

A ruff check or flake8 step fails with a list of CODE violations and exit code 1, while the same code may pass locally on a different linter version or with a different config picked up.

ruff output
app/main.py:10:1: F401 'os' imported but unused
app/main.py:22:80: E501 line too long (94 > 79 characters)
Found 2 errors.

Common causes

Real lint violations

Unused imports (F401), long lines (E501), undefined names (F821) and similar are genuine issues the linter is flagging.

Version/config drift between local and CI

CI installs a different linter version, or reads a different config file, so a rule fires in CI that did not locally - a false "works on my machine."

How to fix it

Auto-fix and review what remains

ruff can fix many issues; review and address the rest.

Terminal
ruff check --fix .
ruff format .
ruff check .   # confirm clean

Pin the linter and centralize config

  1. Pin the exact ruff/flake8 version in dev deps (and pre-commit) so local and CI match.
  2. Keep one config (pyproject.toml/ruff.toml/.flake8) at the repo root.
  3. Configure or # noqa: CODE specific intentional exceptions instead of disabling rules broadly.

How to prevent it

  • Pin linter versions so local and CI agree.
  • Run ruff/flake8 in pre-commit to catch issues before pushing.
  • Keep a single root config and scope ignores narrowly.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →