Repository navigation
fix(realtime): keep item ids within OpenAI's 32-char limit - #7958
Conversation
There was a problem hiding this comment.
Devin Review found 2 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| def _shorten(self, id: Any) -> Any: | ||
| if not isinstance(id, str) or len(id) <= _MAX_ID_LEN: | ||
| return id | ||
| short = _shorten_id(id) | ||
| self._original[short] = id | ||
| return short |
There was a problem hiding this comment.
🟡 Distinct history items share one wire ID
When a 32-character item ID equals _shorten_id of a longer ID, both items use the same wire ID. One create fails as a duplicate, and _original can attribute replies to the wrong item.
Learn more
The item-ID map translates long local IDs to 32-character wire IDs. It leaves existing 32-character IDs unchanged, but _shorten does not reserve those IDs when selecting a hash. The provider therefore cannot distinguish a long ID from a valid short ID matching its hash. Server replies also use the map, so the short ID can be restored as the wrong local ID. update_chat_ctx waits for separate create confirmations, and the added-item handler looks up the restored ID.
Example: Give one item ID long_item_id_abcdefghijklmnopqrstuvwxyz and another item ID equal to _shorten_id('long_item_id_abcdefghijklmnopqrstuvwxyz'). The first ID is hashed, the second stays unchanged, and both creates target the same provider ID instead of creating two history items.
Recommended fix: Maintain a collision-free mapping across all IDs used in a session, including conforming IDs. Reserve or preflight the full history before assigning wire IDs; ensure every wire ID maps to exactly one local ID while keeping IDs of 32 characters or fewer unchanged where possible.
Was this helpful? React with 👍 or 👎 to provide feedback.
| self._original[short] = id | ||
| return short | ||
|
|
||
| def shorten(self, event: dict[str, Any]) -> None: |
There was a problem hiding this comment.
🟡 New ID translation methods lack documentation
The new _shorten, shorten, and restore methods have no docstrings. Repository rules require documentation for every new method.
Learn more
Example: API documentation generated for _ItemIdMap.shorten cannot describe which event fields it rewrites or that it modifies events in place.
Recommended fix: Add concise docstrings to _shorten, shorten, and restore documenting their input, in-place changes, and mapping behavior.
Was this helpful? React with 👍 or 👎 to provide feedback.
The OpenAI Realtime API and Azure OpenAI reject item ids longer than 32 characters (
string_above_max_length). Agents that replay history under their own ids, such as UUIDs oritem_<uuid>_N, lose that item, and every item after it fails withitem_create_invalid_previous_item_idbecause it is anchored to the rejected one. The session only logs "rejected part of a chat context update" and retries on every turn, so the model runs with no history.Now the session shortens over-long item ids where events go out, with the same sha256 hash it already uses for
call_id, and restores the original ids on every server event it receives. The chat context, the remote chat context, and the create and delete waiters keep the ids the agent chose. Ids of 32 characters or fewer go out unchanged. The reconnection replay, which writes to the socket directly, is shortened too.Checked live by replaying history whose first item, with a 36-character id, holds a code word, then asking for the word. Without the fix, the model on direct OpenAI, direct Azure, and LiveKit Inference (Azure and OpenAI) had none of the history and only greeted. With the fix all four, plus xAI through LiveKit Inference, answered with the word, and deleting the item by its long id worked.
Initial prompt and agent context
Model: Claude Opus 5.5
Investigation of Azure realtime sessions rejecting 304 items with ids over 32 characters across 10 projects, plus 121 knock-on "previous item does not exist" errors.