OSError: [Errno 5] Input/output error (tested fix)
OSError: [Errno 5] Input/output error is the OS reporting that a low-level read or write failed. Most often a terminal (pty) went away, but it can also be failing hardware. We reproduced the pty case in Python 3.13 and tested the fix.
1. What Errno 5 is
import errno, os
print(errno.EIO, errno.errorcode[5], os.strerror(5))
Output:
5 EIO Input/output error
EIO is the operating system saying a low-level read or write failed. Python only passes it on. The common causes: a pseudo-terminal (pty) whose other side has closed, a process in the background reading from or writing to a terminal it no longer controls (for example after nohup, & or a closed SSH session), and real hardware trouble: a failing disk, an unplugged USB drive or SD card, or a dropped network mount.
2. Reproduced: reading a pty after the other side closes
import os, pty
master, slave = pty.openpty()
os.write(slave, b"hello\n")
os.close(slave) # the "terminal" side goes away
print(os.read(master, 100)) # buffered data still arrives
try:
os.read(master, 100) # nothing left and no writer
except OSError as e:
print(type(e).__name__, e.errno, e)
Output:
b'hello\r\n'
OSError 5 [Errno 5] Input/output error
The buffered data still arrives (note the \n became \r\n: the terminal driver translates newlines). Once it is drained and no one holds the other end, Linux raises [Errno 5] instead of returning b"" like a pipe would. This is what pexpect-style code and pty.spawn wrappers hit when the child exits.
3. Fix for pty readers: treat EIO as end of output
import os, pty, subprocess
master, slave = pty.openpty()
p = subprocess.Popen(["echo", "done"], stdout=slave, stderr=slave)
os.close(slave)
p.wait()
out = b""
while True:
try:
chunk = os.read(master, 1024)
except OSError as e:
if e.errno == 5: # EIO == child closed the pty: treat as EOF
break
raise
if not chunk:
break
out += chunk
print(out)
Output:
b'done\r\n'
Catch OSError, check e.errno == errno.EIO (5), and stop reading. Re-raise anything else.
4. The other causes (not reproduced here)
- Background job or lost terminal:
input()orprint()from a script started with&, or after its SSH session ended. Redirect stdin from/dev/nulland stdout/stderr to a file (python3 job.py < /dev/null > job.log 2>&1 &), or run it under systemd, tmux or screen. - Disk, USB or SD card: run
dmesg | tailright after the error; I/O error lines naming a device mean hardware or a removed drive. Copy your data off before retrying. - Network mounts (NFS, SMB, sshfs): the connection dropped mid-read; remount and retry.
We did not reproduce these three (they need a real terminal session or failing hardware); the advice is the standard diagnosis. Related: OSError errno codes list and [Errno 9] Bad file descriptor.
Frequently asked questions
What causes [Errno 5] Input/output error in Python?
A pseudo-terminal whose other side closed, a background process using a terminal it lost, or a disk, USB or network-mount failure.
How do I fix Errno 5 when reading from a pty?
Catch OSError, check e.errno == errno.EIO and treat it as end of output.
How do I fix Errno 5 for a background script?
Redirect stdin from /dev/null and output to a log file, or run it under systemd, tmux or screen.
Written with AI assistance. Measured on 2026-10-22 on Debian with Python 3.13.5: errno 5 name and message, reading a pty master after the slave closes, and an EIO-as-EOF read loop around a subprocess. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python is free.