aio-libs/aiohttp · error · TypeError
data argument must be byte-ish (%r)
Error message
data argument must be byte-ish (%r)
What it means
Raised by WebSocketResponse.send_bytes() when the `data` argument is not an instance of (bytes, bytearray, memoryview). This is a TypeError whose %r placeholder prints the actual type, e.g. "data argument must be byte-ish (<class 'str'>)". aiohttp validates before encoding because the underlying send_frame only accepts a concrete byte buffer and would produce a confusing AttributeError otherwise.
Solutions
- If you have text, call send_str() instead of send_bytes().
- Coerce before sending: `await ws.send_bytes(bytes(data))` for buffer-like objects, or `data.encode('utf-8')` for str.
- For JSON produced by an encoder returning bytes (orjson), use send_json_bytes(...) not send_json(...).
- Add an isinstance(data, (bytes, bytearray, memoryview)) assert at the call site during development.
Example fix
// before
await ws.send_bytes({'k': 1}) # raises
await ws.send_bytes('hello') # raises
// after
await ws.send_bytes(json.dumps({'k': 1}).encode())
await ws.send_str('hello')
# or, with orjson:
await ws.send_json_bytes({'k': 1}, dumps=orjson.dumps) Defensive patterns
Strategy: type-guard
Validate before calling
assert isinstance(data, (bytes, bytearray, memoryview)), f'must be byte-ish, got {type(data)}'
await ws.send_bytes(data) Type guard
def is_byteish(data: object) -> bool:
return isinstance(data, (bytes, bytearray, memoryview)) Try / catch
try:
await ws.send_bytes(data)
except TypeError as e:
if 'byte-ish' in str(e):
await ws.send_bytes(bytes(data)) # or send_str if it was text Prevention
- Use send_str for str payloads, send_bytes for byte payloads, send_json for objects.
- If using orjson (bytes output) pair it with send_json_bytes, not send_json.
- Add isinstance asserts in tests around the send call sites.
When it happens
Trigger: Passing a str to send_bytes (use send_str instead), passing an int/None/dict/list, or passing a JSON encoder that yields str when send_json_bytes expects bytes (the dumps result is forwarded verbatim to send_bytes).
Common situations: Mixing up send_str vs send_bytes; using orjson.dumps (which returns bytes) with send_json (which calls send_str) or vice-versa; passing a numpy/array-like object instead of its .tobytes().
Related errors
- Received message : is not WSMsgType.BINARY
- Received message : is not WSMsgType.TEXT
- Cannot call .write() for websocket
- 1002
- 1002
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/4ae9130b44164b9f.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_ws.py:475
"""Send a frame over the websocket."""
if self._writer is None:
raise RuntimeError("Call .prepare() first")
await self._writer.send_frame(message, opcode, compress)
async def send_str(self, data: str, compress: int | None = None) -> None:
if self._writer is None:
raise RuntimeError("Call .prepare() first")
if not isinstance(data, str):
raise TypeError("data argument must be str (%r)" % type(data))
await self._writer.send_frame(
data.encode("utf-8"), WSMsgType.TEXT, compress=compress
)
async def send_bytes(self, data: bytes, compress: int | None = None) -> None:
if self._writer is None:
raise RuntimeError("Call .prepare() first")
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError("data argument must be byte-ish (%r)" % type(data))
await self._writer.send_frame(data, WSMsgType.BINARY, compress=compress)
async def send_json(
self,
data: Any,
compress: int | None = None,
*,
dumps: JSONEncoder = json.dumps,
) -> None:
await self.send_str(dumps(data), compress=compress)
async def send_json_bytes(
self,
data: Any,
compress: int | None = None,
*,
dumps: JSONBytesEncoder,
) -> None:View on GitHub (pinned to d041d4d0fd)