urllib3/urllib3 · error · RuntimeError
must specify at least one of read=True, write=True
Error message
must specify at least one of read=True, write=True
What it means
Raised by select_wait_for_socket when both read and write are False (or omitted). The function is meaningless without at least one direction to monitor. RuntimeError is raised. This is the select(2)-based fallback used on Windows or when poll() is unavailable.
Source
Thrown at src/urllib3/util/wait.py:40
# altogether, for high-numbered file descriptors. The point of poll() is to fix
# that, so on Unixes, we prefer poll().
#
# On Windows, there is no poll() (or at least Python doesn't provide a wrapper
# for it), but that's OK, because on Windows, select() doesn't have this
# strange calling convention; plain select() works fine.
#
# So: on Windows we use select(), and everywhere else we use poll(). We also
# fall back to select() in case poll() is somehow broken or missing.
def select_wait_for_socket(
sock: socket.socket,
read: bool = False,
write: bool = False,
timeout: float | None = None,
) -> bool:
if not read and not write:
raise RuntimeError("must specify at least one of read=True, write=True")
rcheck = []
wcheck = []
if read:
rcheck.append(sock)
if write:
wcheck.append(sock)
# When doing a non-blocking connect, most systems signal success by
# marking the socket writable. Windows, though, signals success by marked
# it as "exceptional". We paper over the difference by checking the write
# sockets for both conditions. (The stdlib selectors module does the same
# thing.)
fn = partial(select.select, rcheck, wcheck, wcheck)
rready, wready, xready = fn(timeout)
return bool(rready or wready or xready)
def poll_wait_for_socket(
sock: socket.socket,View on GitHub (pinned to c8d039c1b7)
Solutions
- Pass at least one of read=True or write=True when invoking the function.
- Prefer the public helpers wait_for_read(sock) or wait_for_write(sock) which set the flag for you.
- Add a guard in wrappers: if not read and not write: raise ValueError before forwarding.
- Audit callers to ensure they forward explicit direction flags.
Example fix
// before ready = wait_for_socket(sock) # raises on Windows/select path // after ready = wait_for_read(sock, timeout=5) # explicit direction
Defensive patterns
Strategy: validation
Validate before calling
def safe_wait_select(sock, read=False, write=False, timeout=None):
if not read and not write:
raise ValueError('need read or write')
return __import__('urllib3.util.wait', fromlist=['select_wait_for_socket']).select_wait_for_socket(sock, read=read, write=write, timeout=timeout) Type guard
def has_direction(read: bool, write: bool) -> bool:
return bool(read or write) Try / catch
from urllib3.util.wait import wait_for_read, wait_for_write
# prefer public helpers; if calling the raw fn:
try:
select_wait_for_socket(sock, read=True, timeout=t)
except RuntimeError:
wait_for_read(sock, timeout=t) Prevention
- Use the public wait_for_read/wait_for_write helpers instead of the low-level functions.
- Always pass read=True or write=True explicitly.
- Add an assertion in wrappers that forward direction flags.
When it happens
Trigger: Calling select_wait_for_socket(sock) with no read/write, calling wait_for_socket(sock) (the dispatcher) with neither flag, or a wrapper that forwards default-False values into this function.
Common situations: Custom socket code that wraps wait_for_socket and forwards optional kwargs without defaults, test harnesses that call the function directly to exercise the select path, or a refactor that dropped the read=True argument.
Related errors
- {match.string!r} is not a valid host
- Invalid proxy scheme for tunneling: {scheme!r}, must be eith
- Tunnel host can't contain control characters %r
- Invalid header name {header_bytes!r}
- Invalid header value {value_bytes!r}
AI-assisted analysis of urllib3/urllib3@c8d039c1b7 (2026-08-04).
Data as JSON: /data/errors/5bf0c5886b243e17.json.
Report an issue: GitHub.