ruff FURB103: fix changes program behavior
Hypothesis Nemotron 3 Super predicted to fire fired 1×
Incorrectly handles non-string/write-incompatible data types; write_text expects str, write_bytes expects bytes, but the original .write() might have relied on coercion or custom __write__.
Program
Output
(no stdout) TypeError: a bytes-like object is required, not 'BytesLike'
(no stdout) TypeError: memoryview: a bytes-like object is required, not 'BytesLike'
- fired
- 1 before · 0 after
- runs
- each version twice, same file name, no network
- minimizer
- 21 oracle evaluations
Judge Nemotron 3 Ultra advisory program-bug · low
The program already fails before the fix because the object lacks a `__bytes__` method, causing a TypeError when `f.write(obj)` is called. The fix changes the error message slightly (from `TypeError: a bytes-like object is required` to `TypeError: memoryview: a bytes-like object is required`) but the program was already broken due to a missing `__bytes__` implementation, unrelated to the rewrite.
Explanation Nemotron 3 Super unverified
The original `open` call uses `'wb'` mode, which expects a file object that accepts bytes; writing `__str__`'s string result would raise a clear TypeError about needing a bytes‑like object. The replacement calls `pathlib.Path(...).write_bytes`, which internally creates a memoryview from the argument before checking its type, so the same string from `__str__` triggers a TypeError that mentions memoryview, altering the error message and thus the observable behavior. Since the exception text differs, the transformation is not semantics‑preserving.
Reproduce
- file
prog.py(the before program)- minimized program
- r1-p007.py
- evidence sheet
- r1-p007.md
- regression fixture
- r1-p007.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py ruff check --preview --isolated --select FURB103 --fix prog.py python3 prog.py
Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra
FURB103 fix · FURB103 file write binary · ruff rule FURB103 pathlib Path write_bytes · FURB103 coercion to bytes
- duplicate astral-sh/ruff/issues/26920 Report 26920 describes the same defect: the FURB103 fix replaces open/write with Path.write_bytes, changing behavior because open truncates the file before write fails on a non-bytes-like object.
- related astral-sh/ruff/issues/26922 Report 26922 covers a similar safety issue for FURB101 (read-whole-file) and mentions FURB103, but the specific construct and error differ.
- related astral-sh/ruff/issues/20785 Report 20785 states the FURB103 fix breaks code, but the example involves json.dumps and a different failure mode.
Details
- program
- FURB103 Hazard 7: Non-String Data Coercion Failure
- hazard
- Incorrectly handles non-string/write-incompatible data types; write_text expects str, write_bytes expects bytes, but the original .write() might have relied on coercion or custom __write__.
- oracle
stderr before: TypeError: a bytes-like object is required, not 'BytesLike' stderr after: TypeError: memoryview: a bytes-like object is required, not 'BytesLike'- run
- 2026-10-27T22-10-17-844Z-aav5-ruff-FURB103
generated program (8 lines) and the fix's diff
--- orig.py 2026-10-27 22:10:35.167580901 +0000 +++ fixed.py 2026-10-27 22:10:39.271807521 +0000 @@ -1,8 +1,8 @@ +import pathlib class BytesLike: def __bytes__(self): return b'converted' def __str__(self): return 'str fall' obj = BytesLike() -with open('coerce.bin', 'wb') as f: - f.write(obj) # works via __bytes__ +pathlib.Path('coerce.bin').write_bytes(obj) # works via __bytes__ with open('coerce.bin', 'rb') as f: print(repr(f.read()))
rule documentation given to the models
# write-whole-file (FURB103)
Derived from the **refurb** linter.
Fix is sometimes available.
This rule is in preview and is not stable. The `--preview` flag is required for use.
## What it does
Checks for uses of `open` and `write` that can be replaced by `pathlib`
methods, like `Path.write_text` and `Path.write_bytes`.
## Why is this bad?
When writing a single string to a file, it's simpler and more concise
to use `pathlib` methods like `Path.write_text` and `Path.write_bytes`
instead of `open` and `write` calls via `with` statements.
## Example
```python
with open("file.txt", "w") as f:
f.write("some text")
```
Use instead:
```python
from pathlib import Path
Path("file.txt").write_text("some text")
```
## Fix Safety
This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression.
## References
- [Python documentation: `Path.write_bytes`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.write_bytes)
- [Python documentation: `Path.write_text`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.write_text)