Repository navigation
Calling os.stat() on a named pipe used by asyncio.ProactorEventLoop.start_serving_pipe() will raise OSError #100573
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Dec 28, 2022 CreateNamedPipeW()creates a pipe instance in a state that allows a client to connect immediately. If the client has already closed the connection when the server tries to manually connect, theConnectNamedPipe()call fails withERROR_NO_DATA(232), for whichBrokenPipeErroris raised1. Anos.stat()client disconnects immediately as soon as it determines that it connected to a pipe file or directory in the named-pipe filesystem2.The
BrokenPipeErrorcan be handled inloop_accept_pipe(). Simply close the pipe, and then continue viaself.call_soon(loop_accept_pipe).Footnotes
-
For some reason that I can't explain, the NT status code
STATUS_PIPE_CLOSINGis mapped to the Windows error codeERROR_NO_DATAinstead ofERROR_BROKEN_PIPE. Python thus maps bothERROR_BROKEN_PIPEandERROR_NO_DATAto POSIXEPIPE, for whichBrokenPipeErroris raised. ↩ -
GetFileAttributesW()is called to determine whether the path name is a directory. This briefly opens another instance of the pipe. Instead,GetFileInformationByHandleEx()could be called to get theFileBasicInfo. We have to be careful with synchronous operations on a pipe, such as querying file information, but there's no risk of deadlock in this case becauseos.stat()is the only user of the pipe instance. ↩
-
No matter what the client does, the server shouldn’t crash. So this is primarily an asyncio bug, right? Changing os.stat() to avoid this wouldn’t be a full fix.
No matter what the client does, the server shouldn’t crash. So this is primarily an asyncio bug, right?
This issue can be resolved by handling
BrokenPipeErrorwhen it's raised by theaccept_pipe()call inloop_accept_pipe(). For example:f = self._proactor.accept_pipe(pipe) except BrokenPipeError: if pipe and pipe.fileno() != -1: pipe.close() self.call_soon(loop_accept_pipe)
I'll have access to a Windows machine next week and I'll try to look into this then. The code looks tricky, and it's old.
I wonder how many other things could cause
OSErrorhere -- the code seems to assume that's pretty bad, because it stops looping (when all goes well,loop_accept_pipeseems to loop viaf.add_done_callback()).PS. The code swallows
CancelledError, which I think should never happen -- but that's not what this issue is about. Also, in that case it doesn't check thatpipe.fileno() != -1before callingpipe.close()-- that seems an oversight too.PS2. Maybe this could be rewritten without the callback function, using a
Taskinstead??OK.
- I confirmed the issue.
- I confirmed that the proposed fix works.
- I looked at the code and since
loop_accept_pipe()is a callback, not a coroutine, I don't see how it could even receive aCancelledError, but that code block is as old as asyncio, so I'm not touching that. - I'm also not going to rewrite this as a
Task. - However, I wonder if at the end of the block handling
OSErrorwe should insertself.call_soon(loop_accept_pipe), since that's the reason the bug is so annoying (if the error happens, the server is not closed or stopped, but it is no longer functional, causing the client to report "All pipe instances are busy"). - It needs a test. I can make one based on the OP's repro.
I ran the reproducer and verified that PR fixes this. I am OK with adding
self.call_soon(loop_accept_pipe)toOSErrorclause, trying again is better than just hanging the server.- added a commit that references this issue
on Jan 13, 2023 Thank you for investigation and correction. I have confirmed that the change in #100959 resolves the issue.
- added a commit that references this issue
on Jun 30, 2026
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Bug report
On Windows, several calls to
os.stat()on the named pipe used byasyncio.ProactorEventLoop.start_serving_pipe()will result in anOSError. At this time, the first call toos.stat()will cause aBrokenPipeErroron the server side.Steps to reproduce
Run the pipe server in Command Prompt:
Run the following script as client on another Command Prompt:
Display the following messages on the server side:
On client side, run
pipe_stat.pyrepeat several times, thenOSErroroccured:This problem seems to stem from commit 9eb3d54 between 3.8.0b3 and 3.8.0b4.
Your environment
Linked PRs