# ruff TC006: 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: TC006 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): real, severity high. The fix quotes the type expression 'MyType', changing it from a runtime-evaluated expression to a forward-reference string. This alters the evaluation order: originally 'MyType' was evaluated first (causing NameError on 'MyType'), but after the fix the second argument '[x]' is evaluated first (causing NameError on 'x'). The documentation only mentions safety regarding multi-line expressions and comments, not evaluation order changes, making this a silent behavior change in plausible code.
- Program: minimized from 8 to 2 lines

## Reproducer

```python
import typing
y = typing.cast(MyType, [x])  # Rule triggers: MyType is not a string
```

## Commands

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

## Observed

```
stderr before: NameError: name 'MyType' is not defined
stderr after: NameError: name 'x' is not defined
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:31:17.911844536 -0400
+++ fixed.py	2026-10-22 13:31:20.063880632 -0400
@@ -1,2 +1,2 @@
 import typing
-y = typing.cast(MyType, [x])  # Rule triggers: MyType is not a string
+y = typing.cast("MyType", [x])  # Rule triggers: MyType is not a string
```

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

The original expression `typing.cast(MyType, [x])` uses the variable `MyType`, which must be defined in scope; if it isn’t, Python raises a `NameError`. After rewriting to `"MyType"`, the first argument becomes a literal string, so the cast succeeds syntactically but still attempts to evaluate `[x]`, requiring `x` to be defined—hence the error shifts from missing `MyType` to missing `x`. Since `cast` expects either a type object or a forward-reference string (used only during static analysis), replacing a required runtime identifier with a string changes when and how names are resolved, altering observable behavior despite preserving the annotation’s intent for type checkers. This violates semantic equivalence because execution now fails on a different undefined name.

## Upstream search

Queries: "TC006 fix", "typing cast first argument type annotation", "ruff tc006 casting non-string literal", "fix behavior quote string in cast"

- related: https://github.com/astral-sh/ruff/issues/13208
  Broad meta-issue about missing rules and overlapping rules for flake8-type-checking, which includes TC006 behavior.
- related: https://github.com/astral-sh/ruff/issues/20096
  Discusses UP045 not triggering inside cast, which interacts with TC006 fixes on the same construct.
- related: https://github.com/astral-sh/ruff/issues/19596
  Shows a chain of fixes (EM101 then TC006) on cast expressions, same rule but different trigger scenario.
- related: https://github.com/astral-sh/ruff/issues/16037
  Infinite loop when fixing cast with format strings, same rule TC006 but different construct causing loop.
- related: https://github.com/astral-sh/ruff/issues/14676
  TC006 being too broad and adding quotes unnecessarily, same rule but broader scope than this specific case.
- related: https://github.com/astral-sh/ruff/issues/1785
  Original feature request to implement flake8-type-checking rules including TC006.
- related: https://github.com/astral-sh/ruff/issues/9573
  About harmonizing TCH/TC rules with flake8-type-checking, includes TC006 scope.
- related: https://github.com/astral-sh/ruff/issues/14554
  TC006 causing a panic/crash, same rule but different failure mode (crash vs behavior change).
- related: https://github.com/astral-sh/ruff/issues/26625
  About TypeForm support for functions like typing.cast, same construct but forward-looking feature request.
- related: https://github.com/astral-sh/ruff/issues/20237
  TC006 shouldn't trigger with from __future__ import annotations, same rule but different context (postponed evaluation).
- related: https://github.com/astral-sh/ruff/issues/4939
  F401 false positive deleting import needed for cast at runtime, same construct (cast) but different rule interaction.
- related: https://github.com/astral-sh/ruff/issues/6650
  Feature request about TYPE_CHECKING blocks and cast imports, same module context.
- related: https://github.com/astral-sh/ruff/pull/16669
  PR stabilizing runtime-cast-value (TC006) among other things, directly addresses the rule.
- related: https://github.com/astral-sh/ruff/issues/18502
  About adding future annotations import to help rules like TC006, same rule ecosystem.

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

- [github, open] TC006 fix creates quoted type expressions with escape sequences — https://github.com/astral-sh/ruff/issues/22131
- [github, open] [flake8-type-checking] Missing rules, autofix strategies, overlapping rules and more — https://github.com/astral-sh/ruff/issues/13208
- [github, closed] UP045 doesn't trigger inside of `cast` — https://github.com/astral-sh/ruff/issues/20096
- [github, closed] EM101 false positive — https://github.com/astral-sh/ruff/issues/19596
- [github, closed] Infinite loop when fixing file with `cast` and format strings — https://github.com/astral-sh/ruff/issues/16037
- [github, closed] unnecessary-literal-union (PYI030) can remove comments without being marked as unsafe — https://github.com/astral-sh/ruff/issues/16317
- [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] Reharmonize `TCH*` rules with flake8-type-checking · Issue #9573 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/9573
- [tavily] Checking file with rule TC006 cause panic · Issue #14554 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/14554
- [github, open] Improve support for arbitrary functions accepting `TypeForm` arguments — https://github.com/astral-sh/ruff/issues/26625
- [github, open] Ruleset Suggestion: wemake-python-styleguide — https://github.com/astral-sh/ruff/issues/3845
- [github, closed] TC006 (runtime-cast-value) shouldn't trigger with `annotations` — https://github.com/astral-sh/ruff/issues/20237
- [github, closed] [red-knot] Test setup utilities — https://github.com/astral-sh/ruff/issues/13789
- [github, closed] [bug] F401 false positive for imports inside functions — https://github.com/astral-sh/ruff/issues/4939
- [github, closed] [Autofix error] My File: test/distributed/elastic/rendezvous/etcd_rendezvous_backend_test.py — https://github.com/astral-sh/ruff/issues/4203
- [tavily] Feature request: TCH003 - divide typing-only imports from typing module · Issue #6650 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/6650
- [tavily] Confine PEP 695 type annotation rewrites (`UP040`) to non-runtime contexts · Issue #6434 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/6434
- [tavily] [ty] Allow `Self` in `ClassVar` type annotation by MatthewMckee4 · Pull Request #23362 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/23362
- [tavily] `RUF031`: Ignore unparenthesized tuples in subscripts when the subscript is obviously a type annotation or type alias by AlexWaygood · Pull Request #12762 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/12762
- [tavily] `ParamSpec` suppresses the return type annotation in E731 fix · Issue #20097 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/20097
- [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
- [github, open] Add autofix that adds `from __future__ import annotations` only when it would be useful — https://github.com/astral-sh/ruff/issues/18502
- [tavily] Feature request: Allow Configurable Quote Styling for f-Strings in Python 3.12+ · Issue #14118 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/14118
- [tavily] Improve backport friendliness of quotes in Python 3.12 f-string placeholders · Issue #11056 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/11056
- [tavily] Implicit concatenated string formatting · Issue #9457 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/9457

</details>
