Repository navigation
Subprocess timeout causes output to be returned as bytes in text mode #87597
Description
Activity
Passing the argument
text=Truetosubprocess.run()is supposed to mean that any captured output of the called process is automatically decoded and retuned to the user as test instead of bytes.However, if you give a timeout and that timeout expires, the raised
subprocess.TimeoutExpiredexception will have the captured output as as bytes even if text mode is enabled.Test output:
bash-5.0$ python3 test_subprocess.py
Version and interpreter information: namespace(_multiarch='x86_64-linux-gnu', cache_tag='cpython-37', hexversion=50792432, name='cpython', version=sys.version_info(major=3, minor=7, micro=7, releaselevel='final', serial=0))
Completed STDOUT Type: <class 'str'>
Completed STDOUT Content: 'Start\nDone\n'
Timeout STDOUT Type: <class 'bytes'>
Timeout STDOUT Content: b'Start\n'- added3.7 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Mar 8, 2021 communicate() is incomplete, so decoding the output may fail. For example, say the encoding is UTF-8, and the last multibyte character sequence (2-4 bytes) is incomplete. Maybe communicate() should always set
stdout_bytesandstderr_bytesattributes on the timeout exception, and, in text mode, try to decode the output asstdoutand/orstderr. If decoding fails, set the decoded value to None.In Windows, run() tries to complete communication, which is dysfunctional in cases. I created bpo-43346 to propose changing the design in Windows, in order to address 3 cases that can cause subprocess.run() to ignore the given timeout. The proposed change also sets an incomplete read of stdout and stderr as bytes objects, regardless of text mode, because I was simply matching what POSIX does in this case.
- added3.8 (EOL)end of lifeend of life3.9 (EOL)end of lifeend of life3.10 (EOL)end of lifeend of lifeand removed3.7 (EOL)end of lifeend of life
on Mar 8, 2021 Eryk Sun: Well, I think step 1 should be to update the documentation for Python 3.7 through 3.10 on
subprocess.run()andsubprocess.TimeoutExpiredto clearly state thatTimeoutExpired.stdoutandTimeoutExpired.stderrwill be in bytes format even if text mode is set.If we went with the model of having
stdout_bytesand attempting to decode intostdout, we'd want an option to ignore a trailing decoding error.Reacted by Gregory P. Smith25 remaining items
- addedpendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is provided
on Jan 5, 2024 - removedpendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is provided3.13only security fixesonly security fixes
on Jan 5, 2024 I don't think this should be closed, from my perspective this is just a bug, and should not have been documented as now changing it would be a breaking change (which would still be warranted in my opinion).
I raised a PR addressing this (#95579 - not sure why it was moved to draft state), and a discussion following the pushback (https://discuss.python.org/t/pr-review-request-decode-subprocess-output-in-text-mode-when-timeout-is-hit/19594/2) which didn't get any input.
This feels very much unfinished to me, and unfortunately remains an ugly wart for users who want to properly handle errors when using subprocess (especially in combination with mypy for type checking).
I understand your frustration, but existing code already depends on the behavior given it has been this way for 11+ years now so it isn't "just a bug" in that sense - thus documenting the existing behavior to save everyone the trouble of discovering it the hard way.
I moved that PR to Draft as most PRs should really just start in Draft mode (a realtively new-to-github feature, we should use it more often) as that helps indicate that there are unresolved larger issues and it is unclear if the change as is, is even what we want. See my #95579 (comment) comment.
It sounds like there is a potential way forward based on the last comment from @zooba on the PR - keeping both the bytes and decoding to text on demand via a new TimeoutExpired attribute for the purpose.
I'm fine reopening this since you seem interested in working on it.
Reacted by Erlend E. Aasland- removedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Mar 13, 2025 - added a commit that references this issue
on Dec 17, 2025 - added a commit that references this issue
on Jun 28, 2026 - added a commit that references this issue
on Jun 30, 2026 - added a commit that references this issue
on Aug 19, 2026
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields: