Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/openai/resources/realtime/realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ async def send_raw(self, data: bytes | str) -> None:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
return
await self._connection.send(data)
try:
await self._connection.send(data)
except Exception:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
Comment on lines +368 to +369
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve byte payloads when queueing failed raw sends

When send_raw() is called with bytes and the socket drops during send, this decodes the payload and stores it in SendQueue, whose flush path later calls send(str). That changes a binary WebSocket frame into a text frame for UTF-8 bytes, and for arbitrary binary bytes (for example audio chunks containing 0xff) raises UnicodeDecodeError before the original connection failure can be re-raised or the message queued. Since send_raw explicitly accepts bytes, the retry path should retain the original frame type instead of forcing UTF-8 text; the same new exception-path conversion appears in the sync and Responses variants as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this is a pre-existing limitation in the _is_reconnecting block as well (same .decode("utf-8") pattern). The SendQueue currently only accepts str, so both code paths use the same conversion.

I matched the existing reconnection path for consistency. Happy to open a follow-up to make SendQueue support bytes | str to fix both paths if that's preferred.

raise

async def close(self, *, code: int = 1000, reason: str = "") -> None:
self._intentionally_closed = True
Expand Down Expand Up @@ -842,7 +847,12 @@ def send_raw(self, data: bytes | str) -> None:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
return
self._connection.send(data)
try:
self._connection.send(data)
except Exception:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
raise

def close(self, *, code: int = 1000, reason: str = "") -> None:
self._intentionally_closed = True
Expand Down
14 changes: 12 additions & 2 deletions src/openai/resources/responses/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3855,7 +3855,12 @@ async def send_raw(self, data: bytes | str) -> None:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
return
await self._connection.send(data)
try:
await self._connection.send(data)
except Exception:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
raise

async def close(self, *, code: int = 1000, reason: str = "") -> None:
self._intentionally_closed = True
Expand Down Expand Up @@ -4312,7 +4317,12 @@ def send_raw(self, data: bytes | str) -> None:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
return
self._connection.send(data)
try:
self._connection.send(data)
except Exception:
raw = data if isinstance(data, str) else data.decode("utf-8")
self._send_queue.enqueue(raw)
raise

def close(self, *, code: int = 1000, reason: str = "") -> None:
self._intentionally_closed = True
Expand Down