ThreadException(OSError(Socket is closed)) when program exits without reading input stream
Author: sporksmithCreated Nov 11, 2025Updated Aug 10, 2026
LabelsBugGroup
When executing a program that exits without reading the input stream, we get a ThreadException, wrapping an OSError, due to the socket being closed when we try to write to it.
To reproduce
Example script:
import fabric
import io
conn = fabric.connection.Connection('localhost')
conn.run('exit 0', in_stream=io.StringIO("blah"))and output:
Traceback (most recent call last):
File "/home/jnewsome/tmp/repro.py", line 5, in <module>
conn.run('exit 0', in_stream=io.StringIO("blah"))
File "/home/jnewsome/.local/lib/python3.10/site-packages/decorator.py", line 235, in fun
return caller(func, *(extras + args), **kw)
File "/home/jnewsome/.local/lib/python3.10/site-packages/fabric/connection.py", line 23, in opens
return method(self, *args, **kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/fabric/connection.py", line 763, in run
return self._run(self._remote_runner(), command, **kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/context.py", line 113, in _run
return runner.run(command, **kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/fabric/runners.py", line 83, in run
return super().run(command, **kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/runners.py", line 395, in run
return self._run_body(command, **kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/runners.py", line 451, in _run_body
return self.make_promise() if self._asynchronous else self._finish()
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/runners.py", line 503, in _finish
raise ThreadException(thread_exceptions)
invoke.exceptions.ThreadException:
Saw 1 exceptions within threads (OSError):
Thread args: {'kwargs': {'echo': None,
'input_': <_io.StringIO object at 0x77872e163d90>,
'output': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>},
'target': <bound method Runner.handle_stdin of <fabric.runners.Remote object at 0x77872c4759c0>>}
Traceback (most recent call last):
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/util.py", line 211, in run
super().run()
File "/usr/lib/python3.10/threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/runners.py", line 877, in handle_stdin
self.write_proc_stdin(data)
File "/home/jnewsome/.local/lib/python3.10/site-packages/invoke/runners.py", line 1017, in write_proc_stdin
self._write_proc_stdin(data.encode(self.encoding))
File "/home/jnewsome/.local/lib/python3.10/site-packages/fabric/runners.py", line 92, in _write_proc_stdin
return self.channel.sendall(data)
File "/home/jnewsome/.local/lib/python3.10/site-packages/paramiko/channel.py", line 844, in sendall
sent = self.send(s)
File "/home/jnewsome/.local/lib/python3.10/site-packages/paramiko/channel.py", line 799, in send
return self._send(s, m)
File "/home/jnewsome/.local/lib/python3.10/site-packages/paramiko/channel.py", line 1196, in _send
raise socket.error("Socket is closed")
OSError: Socket is closedWe get the same output if we change the command from exit 0 to exit 1; i.e. we get the ThreadException/OSError instead of any sort of an UnexpectedExit.
Expected behavior
One of:
- No exception surfaced to the
runcaller at all related to the process not reading the full input. (I'd still expect anUnexpectedExitif it exited with a non-zero status code, whether or not it read the full input first) - Some clearer exception derived from
invoke.failure.Failure; e.g.UnconsumedInput, that can be more-easily caught and handled. Preferably in combination with a parameter torunlikeallow_unconsumed_inputallowing to suppress such errors.
Environment
python is a packaged version on popos:
$ python3 --version
Python 3.10.12
$ apt show python3
Package: python3
Version: 3.10.6-1~22.04.1
...ssh and sshd is openssh:
$ ssh -V
OpenSSH_8.9p1 Ubuntu-3ubuntu0.13, OpenSSL 3.0.2 15 Mar 2022
$ apt show openssh-client
Package: openssh-client
Version: 1:8.9p1-3ubuntu0.13
$ sshd -V
unknown option -- V
OpenSSH_8.9p1 Ubuntu-3ubuntu0.13, OpenSSL 3.0.2 15 Mar 2022
...
$ apt show openssh-server
Package: openssh-server
Version: 1:8.9p1-3ubuntu0.13
...fabric is the current latest:
$ pip show fabric
Name: fabric
Version: 3.2.2Source: fabric/fabric