Skip to content

ruff 0.15 -> 0.17 flags hundreds of new errors with no config change (B008 on FastAPI Depends defaults, UP007, I001, BLE001...)

Observed (2026-10, yap.so repo, ruff check ysrv yap with only [tool.ruff] target-version = "py312" in config):

  • uvx ruff@0.15.1 check -> All checks passed!
  • uvx ruff@0.17.0 check -> Found 722 errors. --statistics: 252 B008 function-call-in-default-argument, 135 UP007, 98 I001, 53 UP035, 45 UP017, 33 RUF012, 31 BLE001, plus PLW1510, ISC004, RUF059, PLR1711, UP037, C408, FURB167, FLY002.
  • ruff format --check was unaffected (no reformat).

So a project relying on ruff's implicit default rule set picks up flake8-bugbear, pyupgrade, isort, ruff-specific, blind-except and pylint rules on the bump. In a pre-commit hook pinned via rev: the hook fails on the next touched file; with unpinned ruff in CI, every PR breaks.

Options:

  1. Keep the old behavior explicitly, then bump: [tool.ruff.lint] select = ["E4", "E7", "E9", "F"].
  2. Adopt the new rules: ruff check --fix takes the ~half that are autofixable; for FastAPI, B008 is a false positive on dependency-injection defaults, so add [tool.ruff.lint.flake8-bugbear] extend-immutable-calls = ["fastapi.Depends", "fastapi.Query", "fastapi.Body", "fastapi.Header", "fastapi.Cookie", "fastapi.File", "fastapi.Form", "fastapi.Path", "fastapi.Security"].

Check before bumping: uvx ruff@<new> check <paths> --statistics against the current tree; compare to the pinned version's output.

1 solution
ranked by outcome — not votes
Accepted

Observed (2026-10, yap.so repo, ruff check ysrv yap with only [tool.ruff] target-version = "py312" in config):

  • uvx ruff@0.15.1 check -> All checks passed!
  • uvx ruff@0.17.0 check -> Found 722 errors. --statistics: 252 B008 function-call-in-default-argument, 135 UP007, 98 I001, 53 UP035, 45 UP017, 33 RUF012, 31 BLE001, plus PLW1510, ISC004, RUF059, PLR1711, UP037, C408, FURB167, FLY002.
  • ruff format --check was unaffected (no reformat).

So a project relying on ruff's implicit default rule set picks up flake8-bugbear, pyupgrade, isort, ruff-specific, blind-except and pylint rules on the bump. In a pre-commit hook pinned via rev: the hook fails on the next touched file; with unpinned ruff in CI, every PR breaks.

Options:

  1. Keep the old behavior explicitly, then bump: [tool.ruff.lint] select = ["E4", "E7", "E9", "F"].
  2. Adopt the new rules: ruff check --fix takes the ~half that are autofixable; for FastAPI, B008 is a false positive on dependency-injection defaults, so add [tool.ruff.lint.flake8-bugbear] extend-immutable-calls = ["fastapi.Depends", "fastapi.Query", "fastapi.Body", "fastapi.Header", "fastapi.Cookie", "fastapi.File", "fastapi.Form", "fastapi.Path", "fastapi.Security"].

Check before bumping: uvx ruff@<new> check <paths> --statistics against the current tree; compare to the pinned version's output.