The fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behaviour calling error_received and continuing to run the Transport · Issue #156784 · python/cpython · GitHub
changed the title [-]This fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behavior raising the exception and continuing to run the Transport[/-][+]This fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behavior calling error_recieved and continuing to run the Transport[/+]on Sep 1, 2026
"""Demonstrates that sending a UDP datagram to a port that isn'tlistening now surfaces a recoverable error consistently on bothSelectorEventLoop and ProactorEventLoop (see gh-91227, gh-127057): - DatagramProtocol.error_received() is called, and the transport keeps working afterwards instead of the read loop dying silently.Windows only. Run with: python snippet.py"""importasyncioimportsocketclassEchoProtocol(asyncio.DatagramProtocol):
defconnection_made(self, transport):
self.transport=transportself.received= []
self.errors= []
defdatagram_received(self, data, addr):
self.received.append(data)
deferror_received(self, exc):
self.errors.append(exc)
defclosed_socket_address():
"""Bind and immediately close a UDP socket to get an address that is guaranteed not to be listening."""sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('127.0.0.1', 0))
addr=sock.getsockname()
sock.close()
returnaddrasyncdefdemo_transport(loop):
print('--- DatagramProtocol.error_received() ---')
sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setblocking(False)
sock.bind(('127.0.0.1', 0))
own_addr=sock.getsockname()
transport, protocol=awaitloop.create_datagram_endpoint(
EchoProtocol, sock=sock)
transport.sendto(b'ping', closed_socket_address())
for_inrange(100):
awaitasyncio.sleep(0.05)
ifprotocol.errors:
breakifprotocol.errors:
print(f'error_received() fired: {protocol.errors[0]!r}')
else:
print('error_received() did NOT fire (timed out waiting)')
# The transport must still work afterwards.transport.sendto(b'still alive', own_addr)
for_inrange(100):
awaitasyncio.sleep(0.05)
ifprotocol.received:
breakprint(f'transport still usable afterwards: {protocol.received!r}')
transport.close()
asyncdefmain(loop):
awaitdemo_transport(loop)
defrun(name, loop):
print(f'\n=== {name} ===')
try:
loop.run_until_complete(main(loop))
finally:
loop.close()
if__name__=='__main__':
run('SelectorEventLoop', asyncio.SelectorEventLoop())
run('ProactorEventLoop', asyncio.ProactorEventLoop())
on 3.14 it reports:
PS C:\Users\tagra\projects\cpython> py -3.14 .\snippet.py
=== SelectorEventLoop ===
--- DatagramProtocol.error_received() ---
error_received() fired: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)
transport still usable afterwards: [b'still alive']
=== ProactorEventLoop ===
--- DatagramProtocol.error_received() ---
error_received() did NOT fire (timed out waiting)
transport still usable afterwards: [b'still alive']
on my patched branch I get:
PS C:\Users\tagra\projects\cpython> ./PCbuild/amd64/python_d.exe snippet.py
=== SelectorEventLoop ===
--- DatagramProtocol.error_received() ---
error_received() fired: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)
transport still usable afterwards: [b'still alive']
=== ProactorEventLoop ===
--- DatagramProtocol.error_received() ---
error_received() fired: ConnectionResetError(22, 'No service is operating at the destination network endpoint on the remote system', None, 1234, None)
transport still usable afterwards: [b'still alive']
changed the title [-]This fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behavior calling error_recieved and continuing to run the Transport[/-][+]This fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behavior calling error_received and continuing to run the Transport[/+]on Sep 1, 2026
changed the title [-]This fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behavior calling error_received and continuing to run the Transport[/-][+]The fix in gh-91227 is incorrect, the SelectorEventLoop has the correct behaviour calling error_received and continuing to run the Transport[/+]on Sep 1, 2026
Activity
graingert commented on Sep 1, 2026
on 3.14 it reports:
on my patched branch I get:
graingert commented on Sep 16, 2026
@kumaraditya303 this isn't fixed yet, so I re-opened it
gh-156784: do not ignore error port unreachable in proactor event loo…
[3.15] gh-156784: do not ignore error port unreachable in proactor ev…