# ruff FURB103: 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: FURB103 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): program-bug, severity low. The original program already fails with OSError: [Errno 9] Bad file descriptor because os.open returns an integer file descriptor, but the built-in open() function expects a path string or path-like object, not a raw file descriptor. The fix changes the error to a TypeError from pathlib.Path, but the program was already broken before the rewrite due to incorrect usage of os.open with open().
- Program: minimized from 9 to 7 lines

## Reproducer

```python
import os
fd = os.open('custom.txt', os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
with open(fd, 'w') as f:
    f.write('via fd')
os.close(fd)
with open('custom.txt') as f:
    print(repr(f.read()))
```

## Commands

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

## Observed

```
stderr before: OSError: [Errno 9] Bad file descriptor
stderr after: TypeError: argument should be a str or an os.PathLike object where __fspath__ returns a str, not 'int'
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-27 22:13:19.730667959 +0000
+++ fixed.py	2026-10-27 22:13:22.434817289 +0000
@@ -1,7 +1,7 @@
 import os
+import pathlib
 fd = os.open('custom.txt', os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
-with open(fd, 'w') as f:
-    f.write('via fd')
+pathlib.Path(fd).write_text('via fd')
 os.close(fd)
 with open('custom.txt') as f:
     print(repr(f.read()))
```

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

The original code opens a file via `os.open`, obtaining an integer file descriptor (`fd`), then uses it directly in `open(fd, 'w')` to create a text stream for writing—this is valid because Python’s built-in `open()` accepts integer file descriptors. The rewrite replaces this with `pathlib.Path(fd).write_text(...)`, but `Path` expects a string or path-like object (e.g., filename), not an integer file descriptor. Passing an `int` to `Path()` raises a `TypeError` because integers do not implement `__fspath__`. Thus, the rewrite fails at runtime where the original succeeded, altering behavior from successful file write to immediate exception. This violates semantic preservation: the fix changes what the program does, not just how it does it. The label “safe” is incorrect—the transformation assumes `fd` is a path, but it is a descriptor, making the rewrite invalid for this use case. No speculation about the tool is needed; the error arises directly from mismatched types.

## Upstream search

Queries: "FURB103 fix", "FURB103 os.open file descriptor usage", "ruff FURB103 Path.write_text integer argument", "FURB103 replace os.open with pathlib"

- no duplicate or related report found among 6 hits

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

- [github, open] FURB103 safe fix drops expressions surrounding write() — https://github.com/astral-sh/ruff/issues/29452
- [github, open] FURB103 fix skips file truncation when the data to write has the wrong type — https://github.com/astral-sh/ruff/issues/26920
- [github, open] `FURB101` and `FURB103` apply safe fixes that cause runtime errors — https://github.com/astral-sh/ruff/issues/26922
- [github, closed] FURB101 and FURB103 shouldn’t apply when the I/O variable is used later — https://github.com/astral-sh/ruff/issues/21483
- [github, closed] [FURB] Path write — https://github.com/astral-sh/ruff/issues/18409
- [github, closed] `FURB103` fixes breaks code — https://github.com/astral-sh/ruff/issues/20785

</details>
