Repository navigation
Description
Bug report
Bug description:
On Windows with Python 3.14.x, starting the REPL with the NUL device as stdin and a pipe as stdout makes _pyrepl print the same traceback in an endless loop. The output never stops. In one real incident, an unattended process wrote 5.38 GB of identical tracebacks to a log file over about 26 minutes before it was killed.
This is easy to trigger by accident. In Git Bash (MSYS), both an empty heredoc and </dev/null give the child process the Windows NUL device as stdin, and os.isatty() returns True for it. So the isatty check in _pyrepl/main.py passes and pyrepl is chosen.
Reproduction (Git Bash on Windows 11, python.org 3.14.3):
$ timeout 3 python - <<'EOF' 2>&1 | wc -c
EOF
14266063That is about 14 MB in 3 seconds. Every iteration prints:
Traceback (most recent call last):
File "...\Lib\_pyrepl\readline.py", line 395, in multiline_input
return reader.readline()
File "...\Lib\_pyrepl\reader.py", line 752, in readline
self.prepare()
File "...\Lib\_pyrepl\historical_reader.py", line 306, in prepare
super().prepare()
File "...\Lib\_pyrepl\reader.py", line 585, in prepare
self.console.prepare()
File "...\Lib\_pyrepl\windows_console.py", line 359, in prepare
self.height, self.width = self.getheightwidth()
File "...\Lib\_pyrepl\windows_console.py", line 410, in getheightwidth
raise WinError(GetLastError())
OSError: [WinError 123] <localized: The filename, directory name, or volume label syntax is incorrect.>
The error code is stale. That matches gh-132888, where ctypes.GetLastError() was used incorrectly. Because the console error is not caught at init time, pyrepl gets as far as the interactive loop. There, the bare except: in run_multiline_interactive_console (simple_interact.py) shows the traceback and retries, so the persistent error turns into an infinite loop.
Comparison (same machine, same command):
printf '' | python -exits 0 with no output (real empty pipe, not a tty).PYTHON_BASIC_REPL=1 python - <<'EOF'/EOFprints the banner and>>>, then exits 0.
Status on branches:
- gh-132888: Fix Windows API error checking in pyrepl.windows_console #144248 (fix for Wrong use of
ctypes.GetLastError()in windows_console.py #132888, merged 2026-02-03) changedwindows_console.pyonmainto useget_last_error()with proper error checking. In windows console: -i parameter and redirected output gives error in 3.13 #132962, the same kind of failure (stdout redirected) now falls back to the basic REPL withwarning: can't use pyrepl: [WinError 6] Invalid descriptor. - The
3.14branch and thev3.14.10tag still contain the old code (raise WinError(GetLastError())atgetheightwidth), and gh-132888: Fix Windows API error checking in pyrepl.windows_console #144248 has no backport.
Request:
- Please backport gh-132888: Fix Windows API error checking in pyrepl.windows_console #144248 (or an equivalent fix) to 3.14.
- Separately, consider bounding the retry loop in
run_multiline_interactive_console. A console error that happens on every iteration should abort, or fall back to the basic REPL, instead of retrying forever.
I have not built main to confirm that this exact case (stdin = NUL, stdout = pipe) falls back correctly there.
CPython versions tested on:
3.14
Operating systems tested on:
Windows
Activity
ctypes.GetLastError()in windows_console.py #132888