python/cpython · error · TypeError

can't write str to binary stream

Error message

can't write str to binary stream

What it means

BytesIO.write (Lib/_pyio.py:957) is a binary stream: it wraps its argument in memoryview(b), which requires a bytes-like object. Passing str fails that contract before any buffering, and the method raises TypeError('can't write str to binary stream') to direct you to an encoding step or a text stream.

Source

Thrown at Lib/_pyio.py:957

        with self._lock:
            if size < 0:
                size = len(self._buffer)
            if len(self._buffer) <= self._pos:
                return b""
            newpos = min(len(self._buffer), self._pos + size)
            b = self._buffer[self._pos : newpos]
            self._pos = newpos
            return b.take_bytes()

    def read1(self, size=-1):
        """This is the same as read.
        """
        return self.read(size)

    def write(self, b):
        if isinstance(b, str):
            raise TypeError("can't write str to binary stream")
        with memoryview(b) as view:
            if self.closed:
                raise ValueError("write to closed file")

            n = view.nbytes  # Size of any bytes-like object
            if n == 0:
                return 0

            with self._lock:
                pos = self._pos
                if pos > len(self._buffer):
                    # Pad buffer to pos with null bytes.
                    self._buffer.resize(pos)
                self._buffer[pos:pos + n] = view
                self._pos += n
            return n

    def seek(self, pos, whence=0):

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Encode strings explicitly: `buf.write(s.encode('utf-8'))`.
  2. For JSON payloads use `json.dumps(obj).encode()` or `json.dumps(obj).encode('utf-8')` before writing.
  3. If the stream should hold text, use io.StringIO (or open the target in text mode) instead of BytesIO.

Example fix

# before
buf = io.BytesIO()
buf.write(json.dumps({'a': 1}))  # str -> TypeError

# after
buf = io.BytesIO()
buf.write(json.dumps({'a': 1}).encode('utf-8'))
Defensive patterns

Strategy: type-guard

Validate before calling

if isinstance(data, str):
    data = data.encode('utf-8')
buf.write(data)

Type guard

def is_bytes_like(v) -> bool:
    return not isinstance(v, str) and isinstance(memoryview(v), memoryview)

Try / catch

try:
    buf.write(data)
except TypeError as e:
    if 'str to binary' in str(e):
        buf.write(data.encode('utf-8'))
    else:
        raise

Prevention

When it happens

Trigger: buf.write('hello') on io.BytesIO; mixing text constants into otherwise-binary writes; passing a value read from a text-mode file into a BytesIO; writing JSON-serialized-as-str (`json.dumps`) instead of bytes.

Common situations: Protocols where some fields are text (usernames, URLs) and others binary; json.dumps returning str being written to binary sockets/buffers; refactoring text logs into a BytesIO-backed collector.

Related errors


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