# ruff TC005: fix changes program behavior

Internal evidence sheet generated by fixhunt. Not an issue text. A human re-verifies, checks the duplicates
below, and writes the upstream report in their own words following the project's AI policy.

- Tool: ruff ruff 0.17.1
- Rule: TC005 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): real, severity high. The rule removes an `if TYPE_CHECKING:` block without ensuring `TYPE_CHECKING` is imported from `typing`. In the original code, the block is never executed because `TYPE_CHECKING` is false at runtime, but the name must still be defined. The fix deletes the block, eliminating the `NameError` and changing the exit status from 1 to 0. The documentation presents the rewrite as a safe removal of dead code, but it is only safe when `TYPE_CHECKING` is properly imported.
- Program: minimized from 9 to 6 lines

## Reproducer

```python
def outer():
	if True:
		pass
	if TYPE_CHECKING:
		pass
outer()
```

## Commands

```sh
python3 prog.py
ruff check --preview --isolated --select TC005 --fix prog.py
python3 prog.py
```

## Observed

```
exit 1 → 0
stderr before: NameError: name 'TYPE_CHECKING' is not defined
stderr after: (none)
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:29:30.470045662 -0400
+++ fixed.py	2026-10-22 13:29:32.523079972 -0400
@@ -1,6 +1,4 @@
 def outer():
 	if True:
 		pass
-	if TYPE_CHECKING:
-		pass
 outer()
```

## Why the rewrite is not equivalent (model draft, verify before use)

The original program defines `outer()` with two conditional blocks; when called, it evaluates both conditions—`True` always executes its body, while `if TYPE_CHECKING:` raises a `NameError` because `TYPE_CHECKING` is undefined in runtime scope. After removing that block via the fix, calling `outer()` only executes the first condition (`if True:`), which does nothing, allowing the function to complete without error. Thus, the behavior changes from raising an exception to returning normally, proving the rewrite is not semantics‑preserving despite being labeled "safe."

## Upstream search

Queries: "TC005 fix", "TC005 remove unused conditional block", "TC005 delete dead if TYPE_CHECKING guard", "TC005 eliminate unreachable type checking import"

- related: https://github.com/astral-sh/ruff/issues/28868
  same rule TC005 but the existing report is about follow-up errors after a TC004 fix, not the removal of an undefined TYPE_CHECKING block
- related: https://github.com/astral-sh/ruff/issues/15681
  same TC003/TC002/TC001 rules and TYPE_CHECKING construct but false positives on imports with manual TYPE_CHECKING and __future__.annotations
- related: https://github.com/astral-sh/ruff/pull/16669
  same TYPE_CHECKING construct and SIM108 rule but about considering all TYPE_CHECKING symbols, not removing undefined blocks
- related: https://github.com/astral-sh/ruff/issues/15526
  same TYPE_CHECKING construct but about self-assignment detection inside blocks, not removal of undefined blocks
- related: https://github.com/astral-sh/ruff/issues/6236
  same TYPE_CHECKING construct but about moving imports under TYPE_CHECKING when added to __all__
- related: https://github.com/astral-sh/ruff/issues/11245
  same TYPE_CHECKING construct but false positive F821 on circular reference, not removal of undefined blocks
- related: https://github.com/astral-sh/ruff/issues/24713
  same TYPE_CHECKING construct but PLR6301 override detection shadowed by else branch
- related: https://github.com/astral-sh/ruff/issues/9197
  same TYPE_CHECKING construct but moving typing imports into block causes linter panic
- related: https://github.com/astral-sh/ruff/issues/9298
  same TYPE_CHECKING construct but F401 false positive on quoted generic parameter moved out of block

<details><summary>All hits</summary>

- [github, open] TC004 runtime-import-in-type-checking-block fixer causes follow up: TC005 empty-type-checking-block + F401 unused-import + I001 unsorted-imports — https://github.com/astral-sh/ruff/issues/28868
- [github, closed] unused-function-argument not silenced by prefixed underscore if variable name is "次" — https://github.com/astral-sh/ruff/issues/28721
- [github, closed] Ruff Server errors when using a VSCode interactive session — https://github.com/astral-sh/ruff/issues/11851
- [github, closed] TC003/TC002/TC001 False positive on manual `TYPE_CHECKING` with `__future__.annotations` — https://github.com/astral-sh/ruff/issues/15681
- [github, closed] `TC006` in `0.8.1` is too broad — https://github.com/astral-sh/ruff/issues/14676
- [github, closed] Implement flake8-type-checking — https://github.com/astral-sh/ruff/issues/1785
- [tavily] Can "Unknown rule selector" be a warning, rather than an error? · Issue #5159 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/5159
- [tavily] Consider all `TYPE_CHECKING` symbols for type-checking blocks by MichaReiser · Pull Request #16669 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/16669
- [tavily] Allow Self-Assignment of variable in TYPE_CHECKING block · Issue #15526 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/15526
- [tavily] Don't move imports under `if TYPE_CHECKING` if they're added to `__all__` · Issue #6236 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/6236
- [tavily] False positive for `F821` on circular reference in `TYPE_CHECKING` block · Issue #11245 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/11245
- [tavily] PLR6301 doesn't detect @override from TYPE_CHECKING · Issue #24713 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/24713
- [tavily] [TCH] moving `typing` imports into `TYPE_CHECKING` block does not work as intended · Issue #9197 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/9197
- [tavily] F401: Ruff Incorrectly Flags Quoted Class used as Generic Parameter as Unused · Issue #9298 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/9298

</details>
