python/cpython · warning · TypeError

write() argument must be str, not {type(s).__name__}

Error message

write() argument must be str, not {type(s).__name__}

What it means

Debug-mode guard in BaseSelectorEventLoop.sock_recv_into(): before scheduling an asynchronous read into a caller-provided buffer, the loop asserts the socket is non-blocking. A blocking socket would make the eventual recv_into call stall the entire event loop, so debug mode converts the latent hang into an immediate ValueError.

Source

Thrown at Lib/_android_support.py:61

            fileno = None

        # The default is surrogateescape for stdout and backslashreplace for
        # stderr, but in the context of an Android log, readability is more
        # important than reversibility.
        kwargs.setdefault("encoding", "UTF-8")
        kwargs.setdefault("errors", "backslashreplace")

        super().__init__(BinaryLogStream(prio, tag, fileno), **kwargs)
        self._lock = RLock()
        self._pending_bytes = []
        self._pending_bytes_count = 0

    def __repr__(self):
        return f"<TextLogStream {self.buffer.tag!r}>"

    def write(self, s):
        if not isinstance(s, str):
            raise TypeError(
                f"write() argument must be str, not {type(s).__name__}")

        # In case `s` is a str subclass that writes itself to stdout or stderr
        # when we call its methods, convert it to an actual str.
        s = str.__str__(s)

        # We want to emit one log message per line wherever possible, so split
        # the string into lines first. Note that "".splitlines() == [], so
        # nothing will be logged for an empty string.
        with self._lock:
            for line in s.splitlines(keepends=True):
                while line:
                    chunk = line[:MAX_CHARS_PER_WRITE]
                    line = line[MAX_CHARS_PER_WRITE:]
                    self._write_chunk(chunk)

        return len(s)

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Call sock.setblocking(False) on every socket handed to sock_recv_into
  2. Create sockets inside async code with blocking off from the start
  3. Audit shared connection pools so all consumers agree on blocking mode; add an assertion sock.gettimeout() == 0 in test doubles

Example fix

# before
sock.settimeout(5)
await loop.sock_recv_into(sock, buf)  # debug: ValueError
# after
sock.setblocking(False)
await loop.sock_recv_into(sock, buf)
Defensive patterns

Strategy: validation

Validate before calling

def nb(sock):
    if sock.gettimeout() != 0:
        sock.setblocking(False)
    return sock

n = await loop.sock_recv_into(nb(sock), buf)

Try / catch

try:
    await loop.sock_recv_into(sock, buf)
except ValueError as e:
    if 'non-blocking' in str(e):
        sock.setblocking(False)
        return await loop.sock_recv_into(sock, buf)
    raise

Prevention

When it happens

Trigger: await loop.sock_recv_into(sock, buf) with loop debug enabled and sock.gettimeout() != 0 — i.e. blocking default sockets or those configured with settimeout(n). Common when reusing sockets established by synchronous helper APIs.

Common situations: High-throughput readers migrated from blocking recv_into code that never set non-blocking mode; benchmarking under debug builds; socket pools created by sync connection factories feeding into async consumers.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/6d87c31a67ab1ed2. Report an issue: GitHub.