python / python/cpython

Pipe inheritance broken on Windows since 3.13

Open
#133,506 14 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

3.13 3.14 OS-windows type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

Passing an inheritable pipe file descriptor to a child process is somehow broken on Windows since Python 3.13. Opening the file descriptor in the child process crashes with [WinError 6] the handle is invalid.

> py -3.12-64 .\main.py
INFO:root:S: Starting
INFO:root:Duration for 10000 requests: 0.37447249999968335 s
INFO:root:S: EOF Quitting
> py -3.13-64 .\main.py
INFO:root:S: Starting
Traceback (most recent call last):
  File "D:\temp\test-pipe-main\sub.py", line 31, in <module>
    main()
    ~~~~^^
  File "D:\temp\test-pipe-main\sub.py", line 14, in main
    reply_write_fobj = os.fdopen(reply_write_fd, "wb", buffering=0)
  File "<frozen os>", line 1068, in fdopen
OSError: [WinError 6] Das Handle ist ungültig

Minimal example

import logging
import os
import pickle
import sys
import time


def main():
    logging.basicConfig(level=logging.INFO)
    reply_read_fd, reply_write_fd = os.pipe()
    request_read_fd, request_write_fd = os.pipe()
    child_fds = [reply_write_fd, request_read_fd]
    for fd in child_fds:
        os.set_inheritable(fd, True)

    reply_read_fobj = os.fdopen(reply_read_fd, "rb", buffering=0)
    request_write_fobj = os.fdopen(request_write_fd, "wb", buffering=0)

    child_pid = os.spawnl(
        os.P_NOWAIT,
        sys.executable,
        os.path.basename(sys.executable),
        "sub.py",
        *(str(fd) for fd in child_fds),
    )
    for fd in child_fds:
        os.close(fd)

    number_requests = 10000
    start = time.perf_counter()
    for i in range(number_requests):
        request = f"test{i}"
        pickle.dump(request, request_write_fobj)
        logging.debug(f"C: Sent {request}")
        reply = pickle.load(reply_read_fobj)
        logging.debug(f"C: Got {reply}")

    duration = time.perf_counter() - start
    logging.info(f"Duration for {number_requests} requests: {duration} s")
    request_write_fobj.close()
    os.waitpid(child_pid, 0)
    time.sleep(1)


if __name__ == "__main__":
    main()
import logging
import os
import pickle
import sys


def main():
    logging.basicConfig(level=logging.INFO)
    reply_write_fd = int(sys.argv[1])
    request_read_fd = int(sys.argv[2])

    logging.info("S: Starting")

    reply_write_fobj = os.fdopen(reply_write_fd, "wb", buffering=0)
    request_read_fobj = os.fdopen(request_read_fd, "rb", buffering=0)
    logging.debug("S: Sent event")
    while True:
        try:
            request: str = pickle.load(request_read_fobj)
        except EOFError:
            logging.info("S: EOF Quitting")
            return
        logging.debug(f"S: Handle {request}")
        reply = "test"
        pickle.dump(reply, reply_write_fobj)

        logging.debug("S: Sent event")


if __name__ == "__main__":
    main()
CPython versions tested on:

3.12, 3.13

Operating systems tested on:

Windows

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided main.py and sub.py on Windows with Python 3.12 and 3.13, focusing on os.pipe, os.set_inheritable, os.spawnl, and os.fdopen. Trace the Windows process and pipe-inheritance path to identify why the child receives an invalid handle, then verify the fix with a regression test covering the minimal example.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.