# ruff W605: 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: W605 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): intended-change, severity low. The rule rewrites the string literal containing an invalid escape sequence '\_' into a raw string r'\_' to eliminate the SyntaxWarning, which is the documented purpose. The stderr difference (warning disappears) is exactly the intended semantic change, not a behavioral bug.
- Program: minimized from 8 to 2 lines

## Reproducer

```python
def f(x=['\_']):
    x[0] = 'changed'
```

## Commands

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

## Observed

```
stderr before: /w/prog.py:1: SyntaxWarning: invalid escape sequence '\_'
stderr after: (none)
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:32:04.305623190 -0400
+++ fixed.py	2026-10-22 13:32:06.476659653 -0400
@@ -1,2 +1,2 @@
-def f(x=['\_']):
+def f(x=[r'\_']):
     x[0] = 'changed'
```

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

The original default argument `['\_']` contains a backslash followed by underscore; Python interprets `\_` as an invalid escape sequence but still treats it as two literal characters: backslash then underscore. After the rewrite, `r'\_'` is a raw string where the backslash is preserved literally, so the list becomes `[r'\_']`, which is identical in content—both represent a single string consisting of a backslash and an underscore. However, the original code triggered a SyntaxWarning due to the invalid escape, while the raw string suppresses it. The functional behavior of the list mutation (`x[0] = 'changed'`) remains unchanged because the string value is the same in both cases. Thus, the rewrite does not alter program semantics—it only eliminates a warning. The observed difference is purely diagnostic, not behavioral. Any claim of changed behavior must stem from reliance on the warning’s presence, which is not part of the program’s semantics.

## Upstream search

Queries: "W605 fix", "W605 mutable default argument list literal", "W605 function parameter default value escaping backslash underscore", "W605 def f x equals bracket backslash underscore bracket"

- related: https://github.com/astral-sh/ruff/issues/11491
  same W605 rule but different construct (f-string vs default argument)
- duplicate: https://github.com/astral-sh/ruff/issues/11461
  same W605 unsafe autofix that changes behavior by adding raw prefix to default argument string
- related: https://github.com/astral-sh/ruff/issues/10434
  W605 panic/crash on f-string, different construct than default argument
- related: https://github.com/astral-sh/ruff/issues/6987
  W605 autofix error but with ANN401, different trigger
- related: https://github.com/astral-sh/ruff/issues/5061
  feature request to use raw strings for W605 fix, same rule but not a bug report
- related: https://github.com/astral-sh/ruff/pull/5352
  PR implementing raw-string fix for W605, same rule but not the bug report

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

- [github, open] Wrong fix for W605 in f-strings — https://github.com/astral-sh/ruff/issues/11491
- [github, open] Implement remaining `pycodestyle` rules — https://github.com/astral-sh/ruff/issues/2402
- [github, open] Include some of the checks from flake8-literal — https://github.com/astral-sh/ruff/issues/8616
- [github, closed] unused-function-argument not silenced by prefixed underscore if variable name is "次" — https://github.com/astral-sh/ruff/issues/28721
- [github, open] Implement Pylint — https://github.com/astral-sh/ruff/issues/970
- [github, closed] ISC002 makes W605 unfixable on playground — https://github.com/astral-sh/ruff/issues/19655
- [github, closed] W605 fix is unsafe — https://github.com/astral-sh/ruff/issues/11461
- [github, closed] Ruff does not clean up unused import if it has same name like object attribute — https://github.com/astral-sh/ruff/issues/12775
- [github, closed] Pack of fuzzer Autofix/Infinite loop errors [2] — https://github.com/astral-sh/ruff/issues/8402
- [github, closed] Rule W605 cause panic — https://github.com/astral-sh/ruff/issues/10434
- [tavily] Rules ANN401,W605 cause autofix error · Issue #6987 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/6987
- [tavily] Change W605 autofix to use raw strings if possible · Issue #5061 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/5061
- [tavily] Ruff should warn on all CPython SyntaxWarnings · Issue #20196 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/20196
- [tavily] Implement W605, invalid escape sequence · Issue #474 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/474
- [tavily] Should W605 flag certain byte string escapes? · Issue #19543 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/19543
- [tavily] Format breaks f-string with literal strings inside (versions 0.3.0 and 0.3.1) · Issue #10273 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/10273
- [tavily] Change W605 autofix to use raw strings if possible by hauntsaninja · Pull Request #5352 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/5352

</details>
