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

  1. Pass at least one of read=True or write=True when invoking the function.
  2. Prefer the public helpers wait_for_read(sock) or wait_for_write(sock) which set the flag for you.
  3. Add a guard in wrappers: if not read and not write: raise ValueError before forwarding.
  4. 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

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


AI-assisted analysis of urllib3/urllib3@c8d039c1b7 (2026-08-04). Data as JSON: /data/errors/5bf0c5886b243e17.json. Report an issue: GitHub.