Skip to content

Parallel runs expose usage of closed fds #3975

Description

@vit-zikmund

Issue

Running tox in parallel intermittently exposes operation on a closed fd. That results in various intermittent errors like Bad file descriptor or Input/output error.

This is broken on the very current release 4.56.2. Previous releases (e.g. 4.56.1) don't manifest this error.

Environment

Provide at least:

  • OS: Fedora Linux 43
Output of uv pip list of the host Python, where tox is installed
Package          Version Editable project location
---------------- ------- ----------------------------------------------------
cachetools       7.1.4
colorama         0.4.6
coverage         7.15.0
distlib          0.4.3
filelock         3.29.7
iniconfig        2.3.0
packaging        26.2
platformdirs     4.10.0
pluggy           1.6.0
pygments         2.20.0
pyproject-api    1.10.1
pytest           9.1.1
pytest-asyncio   1.4.0
pytest-cov       7.1.0
pytest-mock      3.15.1
python-discovery 1.4.3
test-test        0.1.0   /tmp/test-test2
tomli-w          1.2.0
tox              4.56.2
tox-uv-bare      1.33.4
virtualenv       21.6.0

Output of running tox

Output of uv run -- tox -rvv --parallel auto -e 3.10,3.11,3.12,3.13,3.14
3.10: 174 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
3.11: 181 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
3.12: 191 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
3.13: 200 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
3.14: 209 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
current: 216 D using system uv from PATH: ~/.local/bin/uv (uv 0.11.6 (x86_64-unknown-linux-gnu)) [tox_uv/_venv.py:248]
3.10: FAIL ✖ in 0.11 seconds
3.10: 217 W remove tox env folder /tmp/test-test2/.tox/3.10 [tox/tox_env/api.py:348]
3.10: 271 W venv> ~/.local/bin/uv venv -p cpython3.10 --allow-existing '--prompt=test-test2[3.10]' -v --python-preference system /tmp/test-test2/.tox/3.10 [tox/tox_env/api.py:485]
3.10: 331 C exit 101 (0.06 seconds) /tmp/test-test2> ~/.local/bin/uv venv -p cpython3.10 --allow-existing '--prompt=test-test2[3.10]' -v --python-preference system /tmp/test-test2/.tox/3.10 pid=617259 [tox/execute/api.py:308]
3.14: OK ✔ in 1.25 seconds
⠼ [3/5] 3.11 | 3.12 | 3.13DEBUG uv 0.11.6 (x86_64-unknown-linux-gnu)
DEBUG Found project root: `/tmp/test-test2`
DEBUG No workspace root found, using project root
DEBUG Using Python request `cpython@3.10` from explicit request
DEBUG Using request connect timeout of 10s and read timeout of 30s
Exception in thread tox-r-err-617478-20:
Traceback (most recent call last):
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/threading.py", line 1082, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/threading.py", line 1024, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.venv/lib/python3.14/site-packages/tox/execute/local_sub_process/read_via_thread_unix.py", line 32, in _read_stream
    self._read_until_eof(selector)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/tmp/test-test2/.venv/lib/python3.14/site-packages/tox/execute/local_sub_process/read_via_thread_unix.py", line 39, in _read_until_eof
    ready = selector.select(timeout=TIMEOUT_FOR_INTERRUPT)
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/selectors.py", line 452, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
OSError: [Errno 9] Bad file descriptor
3.13: FAIL ✖ in 1.46 seconds
DEBUG uv 0.11.6 (x86_64-unknown-linux-gnu)
DEBUG Found project root: `/tmp/test-test2`
DEBUG No workspace root found, using project root
DEBUG Using Python request `cpython@3.13` from explicit request
DEBUG Using request connect timeout of 10s and read timeout of 30s
DEBUG Searching for CPython 3.13 in search path or managed installations
DEBUG Found `cpython-3.14.2-linux-x86_64-gnu` at `/tmp/test-test2/.venv/bin/python3` (first executable in the search path)
DEBUG Ignoring Python interpreter at `/tmp/test-test2/.venv/bin/python3`: system interpreter required
DEBUG Found `cpython-3.14.2-linux-x86_64-gnu` at `/tmp/test-test2/.venv/bin/python` (search path)
DEBUG Ignoring Python interpreter at `/tmp/test-test2/.venv/bin/python`: system interpreter required
DEBUG Found `cpython-3.14.6-linux-x86_64-gnu` at `/usr/bin/python3` (search path)
DEBUG Skipping interpreter at `/usr/bin/python3` from search path: does not satisfy request `3.13`
DEBUG Found `cpython-3.14.6-linux-x86_64-gnu` at `/usr/bin/python` (search path)
DEBUG Skipping interpreter at `/usr/bin/python` from search path: does not satisfy request `3.13`
DEBUG Searching for managed installations at `~/.local/share/uv/python`
DEBUG Skipping managed installation `cpython-3.14.2-linux-x86_64-gnu`: does not satisfy `3.13`
DEBUG Found managed installation `cpython-3.13.13-linux-x86_64-gnu`
DEBUG Found `cpython-3.13.13-linux-x86_64-gnu` at `~/.local/share/uv/python/cpython-3.13-linux-x86_64-gnu/bin/python3.13` (managed installations)
DEBUG Skipping managed installation cpython-3.13.13-linux-x86_64-gnu: system installation preferred
DEBUG Allowing managed installation cpython-3.13.13-linux-x86_64-gnu: no system installations
Using CPython 3.13.13
Creating virtual environment at: .tox/3.13
DEBUG Assessing Python executable as base candidate: ~/.local/share/uv/python/cpython-3.13-linux-x86_64-gnu/bin/python3.13
DEBUG Using base executable for virtual environment: ~/.local/share/uv/python/cpython-3.13-linux-x86_64-gnu/bin/python3.13
DEBUG Allowing existing directory due to `--allow-existing`
DEBUG Using symlink directory ~/.local/share/uv/python/cpython-3.13-linux-x86_64-gnu instead of base Python path: ~/.local/share/uv/python/cpython-3.13-linux-x86_64-gnu/bin/python3.13
DEBUG Detected parent process ID: 617205
DEBUG Parent process executable: ~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/bin/python3.14
DEBUG Parent process comm: tox
DEBUG Could not determine shell from parent process
DEBUG uv 0.11.6 (x86_64-unknown-linux-gnu)
DEBUG Found project root: `/tmp/test-test2`
DEBUG No workspace root found, using project root
DEBUG Using Python request `cpython@3.13` from explicit request
DEBUG Checking for Python environment at: `.tox/3.13`
DEBUG The project environment's Python version satisfies the request: `CPython 3.13`
DEBUG Using request connect timeout of 10s and read timeout of 30s
DEBUG Computed cache info: Timestamp(SystemTime { tv_sec: 1783518335, tv_nsec: 142410223 }), None, None, {}, {"src": Some(Timestamp(Timestamp(SystemTime { tv_sec: 1783518057, tv_nsec: 895063180 })))}. Most recently modified: pyproject.toml
DEBUG Directory source requirement already cached: test-test==0.1.0 (from file:///tmp/test-test2)
DEBUG Registry requirement already cached: pytest==9.1.1
DEBUG Registry requirement already cached: pytest-asyncio==1.4.0
DEBUG Registry requirement already cached: pytest-cov==7.1.0
DEBUG Registry requirement already cached: pytest-mock==3.15.1
DEBUG Registry requirement already cached: iniconfig==2.3.0
DEBUG Registry requirement already cached: packaging==26.2
DEBUG Registry requirement already cached: pluggy==1.6.0
DEBUG Registry requirement already cached: pygments==2.20.0
DEBUG Identified uncached distribution: coverage==7.15.0
DEBUG No cache entry for: https://files.pythonhosted.org/packages/a1/fa/b70062750686bd7da454da27927622f48bbac6990ac7a4c4a4653e7b0036/coverage-7.15.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
DEBUG Sending fresh GET request for: https://files.pythonhosted.org/packages/a1/fa/b70062750686bd7da454da27927622f48bbac6990ac7a4c4a4653e7b0036/coverage-7.15.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
Prepared 1 package in 299ms
Installed 10 packages in 10ms
 + coverage==7.15.0
 + iniconfig==2.3.0
 + packaging==26.2
 + pluggy==1.6.0
 + pygments==2.20.0
 + pytest==9.1.1
 + pytest-asyncio==1.4.0
 + pytest-cov==7.1.0
 + pytest-mock==3.15.1
 + test-test==0.1.0 (from file:///tmp/test-test2)
Traceback (most recent call last):
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/main.py", line 328, in wrap_session
    config.hook.pytest_sessionstart(session=session)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_hooks.py", line 512, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 167, in _multicall
    raise exception
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 139, in _multicall
    teardown.throw(exception)
    ~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/logging.py", line 795, in pytest_sessionstart
    return (yield)
            ^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 121, in _multicall
    res = hook_impl.function(*args)
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/terminal.py", line 852, in pytest_sessionstart
    self.write_sep("=", "test session starts", bold=True)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/terminal.py", line 566, in write_sep
    self._tw.sep(sep, title, fullwidth, **markup)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 152, in sep
    self.line(line, **markup)
    ~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 184, in line
    self.write("\n")
    ~~~~~~~~~~^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 164, in write
    self.write_raw(msg, flush=flush)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 168, in write_raw
    self._file.write(msg)
    ~~~~~~~~~~~~~~~~^^^^^
OSError: [Errno 5] Input/output error

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/test-test2/.tox/3.13/bin/pytest", line 10, in <module>
    sys.exit(_console_main())
             ~~~~~~~~~~~~~^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/config/__init__.py", line 253, in _console_main
    code = _main(prog=_get_prog_name(sys.argv))
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/config/__init__.py", line 229, in _main
    ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config)
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_hooks.py", line 512, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 167, in _multicall
    raise exception
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 121, in _multicall
    res = hook_impl.function(*args)
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/main.py", line 377, in pytest_cmdline_main
    return wrap_session(config, _main)
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/main.py", line 350, in wrap_session
    config.notify_exception(excinfo, config.option)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1267, in notify_exception
    res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo)
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_hooks.py", line 512, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 167, in _multicall
    raise exception
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/pluggy/_callers.py", line 121, in _multicall
    res = hook_impl.function(*args)
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/terminal.py", line 582, in pytest_internalerror
    self.write_line("INTERNALERROR> " + line)
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/terminal.py", line 538, in write_line
    self._tw.line(line, **markup)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 184, in line
    self.write("\n")
    ~~~~~~~~~~^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 164, in write
    self.write_raw(msg, flush=flush)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.tox/3.13/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py", line 168, in write_raw
    self._file.write(msg)
    ~~~~~~~~~~~~~~~~^^^^^
OSError: [Errno 5] Input/output error
Exception ignored on flushing sys.stdout:
OSError: [Errno 5] Input/output error
Exception in thread tox-r-out-617498-5:
Traceback (most recent call last):
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/threading.py", line 1082, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/threading.py", line 1024, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test-test2/.venv/lib/python3.14/site-packages/tox/execute/local_sub_process/read_via_thread_unix.py", line 32, in _read_stream
    self._read_until_eof(selector)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/tmp/test-test2/.venv/lib/python3.14/site-packages/tox/execute/local_sub_process/read_via_thread_unix.py", line 39, in _read_until_eof
    ready = selector.select(timeout=TIMEOUT_FOR_INTERRUPT)
  File "~/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/lib/python3.14/selectors.py", line 452, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
OSError: [Errno 9] Bad file descriptor
3.13: 219 W remove tox env folder /tmp/test-test2/.tox/3.13 [tox/tox_env/api.py:348]
3.13: 263 W venv> ~/.local/bin/uv venv -p cpython3.13 --allow-existing '--prompt=test-test2[3.13]' -v --python-preference system /tmp/test-test2/.tox/3.13 [tox/tox_env/api.py:485]
3.13: 325 I exit 0 (0.06 seconds) /tmp/test-test2> ~/.local/bin/uv venv -p cpython3.13 --allow-existing '--prompt=test-test2[3.13]' -v --python-preference system /tmp/test-test2/.tox/3.13 pid=617239 [tox/execute/api.py:308]
3.13: 326 W uv-sync> ~/.local/bin/uv sync --python-preference system -v --link-mode symlink --frozen --no-default-groups --group unit-test --all-extras --color never --no-progress -p cpython3.13 [tox/tox_env/api.py:485]
3.13: 678 I exit 0 (0.35 seconds) /tmp/test-test2> ~/.local/bin/uv sync --python-preference system -v --link-mode symlink --frozen --no-default-groups --group unit-test --all-extras --color never --no-progress -p cpython3.13 pid=617301 [tox/execute/api.py:308]
3.13: 679 W commands[0]> pytest -v -p no:cacheprovider --cov=. --cov-config=pyproject.toml --cov-report=term-missing:skip-covered --junit-prefix 3.13 --color=no tests [tox/tox_env/api.py:485]
3.13: 1683 C exit 120 (1.00 seconds) /tmp/test-test2> pytest -v -p no:cacheprovider --cov=. --cov-config=pyproject.toml --cov-report=term-missing:skip-covered --junit-prefix 3.13 --color=no tests pid=617483 [tox/execute/api.py:308]
3.12: OK ✔ in 1.54 seconds
  3.10: FAIL code 101 (0.11 seconds)
  3.11: OK (1.55=setup[0.48]+cmd[1.07] seconds)
  3.12: OK (1.54=setup[0.44]+cmd[1.11] seconds)
  3.13: FAIL code 120 (1.46=setup[0.46]+cmd[1.00] seconds)
  3.14: OK (1.25=setup[0.21]+cmd[1.04] seconds)
  evaluation failed :( (1.63 seconds)

Minimal example

Use the attached recreate project and inside it run

uv run -- tox -rvv --parallel auto  -e 3.10,3.11,3.12,3.13,3.14

# running the tests one at a time works ok
uv run -- tox -rvv --parallel 1  -e 3.10,3.11,3.12,3.13,3.14

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions