| Feature | Description |
|---|---|
| Issue | Sockets hanging in CLOSE_WAIT state after migration to Python 3.13. |
| Symptom | WiFi drops, network latency, and “Too many open files” errors. |
| Primary Cause | Changes in garbage collection and socket lifecycle management in Python 3.13. |
| Resolution | Explicit socket closing, context managers, and timeout adjustments. |

What is the Python 3.13 Socket CLOSE_WAIT Issue?
The Python 3.13 socket CLOSE_WAIT issue refers to a state where a network connection remains “half-closed.” In this state, the remote server has closed the connection, but the local Python application fails to acknowledge it or close its end of the socket.
With the release of Python 3.13, changes to the internal garbage collector and the asyncio event loop have made implicit socket closing less reliable. When sockets leak in this manner, they consume file descriptors.
For users on WiFi, this often manifests as a “WiFi drop.” The operating system runs out of available sockets for the network interface, causing the wireless driver to reset or fail to establish new handshakes.
Step-by-Step Solutions
1. Monitor Socket States
First, confirm that your Python processes are indeed leaking sockets. Use the following command to check for connections stuck in the CLOSE_WAIT state.
netstat -an | grep CLOSE_WAIT
# Or using the more modern ss tool
ss -tap | grep CLOSE_WAIT
2. Enforce Context Managers
Relying on the garbage collector to close sockets is risky in Python 3.13. You must ensure all socket operations use a with statement to guarantee closure even if an exception occurs.
# Example of proper socket handling
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("example.com", 80))
s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
data = s.recv(1024)
3. Update Asyncio Implementation
If you are using asyncio, ensure you are explicitly closing writers. Python 3.13 is more sensitive to unclosed transport streams which frequently lead to the CLOSE_WAIT loop.
# Ensure writer is closed in finally block
try:
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
finally:
writer.close()
await writer.wait_closed()
4. Adjust System-Level Timeouts
If the application-level fixes are insufficient, you can force the operating system to reclaim sockets faster. This is a workaround for the WiFi drop symptoms while you refactor code.
# Reduce the keepalive time (Linux)
sudo sysctl -w net.ipv4.tcp_keepalive_time=60
sudo sysctl -w net.ipv4.tcp_fin_timeout=30