Realtime API: input transcription completion references a nonexistent item after response.cancel and input audio commit

Environment

  • Surface: API, direct WebSocket connection from Python on macOS.
  • Endpoint: wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1
  • Model: gpt-realtime-2.1
  • Input transcription model: gpt-transcribe
  • Client library: websocket-client 1.9.2; Python 3.14.7.
  • Audio: Mono PCM16, 24 kHz.
  • Turn detection: server_vad, threshold 0.7, silence duration 500 ms.
  • Noise reduction: far_field.

Bug

During shutdown, the server committed user audio as item A, but emitted conversation.item.input_audio_transcription.completed with item B.

Retrieving both items on the same connection showed:

  • A existed and contained the same final transcript received in B’s transcription event.
  • B did not exist, according to the server’s retrieval error.
  • A also contained four seconds of audio, matching the duration of the uploaded excerpt.

The IDs were:

A: item_ENekpCNlm80W0ngS6jyJx
B: item_ENektOWilcj4Deo8TJ5NY

Expected: The transcription completion should identify the committed user audio item. OpenAI’s transcription guide instructs clients to match transcription events to committed items using item_id.

This mismatch prevents our client from resolving A’s pending transcription. It eventually reaches its shutdown timeout and displays an unavailable-transcript placeholder followed by the transcript associated with B.

Cancellation or its timing may be involved, but the cause is not confirmed.

Reproduction

  1. Open a WebSocket session with the configuration above and wait for the initial session.updated.
  2. Stream a four-second speech excerpt in 100 ms chunks at real-time speed.
  3. Stop uploading while the utterance is unfinished. The server has emitted input_audio_buffer.speech_started, but no speech_stopped.
  4. Send these three separate events consecutively, without waiting for acknowledgements between them:
{"type":"session.update","session":{"type":"realtime","audio":{"input":{"turn_detection":null}}}}
{"type":"response.cancel"}
{"type":"input_audio_buffer.commit","event_id":"final_input_commit"}
  1. Keep the connection open and process incoming events for up to ten seconds.
  2. Upon receiving transcription completion, retrieve both the committed item ID and the transcription event’s item ID:
{"type":"conversation.item.retrieve","item_id":"item_ENekpCNlm80W0ngS6jyJx","event_id":"retrieve_committed_item"}
{"type":"conversation.item.retrieve","item_id":"item_ENektOWilcj4Deo8TJ5NY","event_id":"retrieve_transcribed_item"}

Observed timeline — seconds relative to replay start; summarized from received events:

Time Event
5.329 session.updated confirms turn_detection: null
5.330 Cancellation returns response_cancel_not_active
5.330 input_audio_buffer.committed, conversation.item.added, and conversation.item.done identify A
5.753 conversation.item.input_audio_transcription.completed identifies B
6.321 Retrieval of A returns the same final transcript and four seconds of audio
6.323 Retrieval of B fails because B does not exist

Control tests using the same audio excerpt:

  • Omitting only response.cancel, while retaining the session update and final commit, produced matching item IDs. This was one successful control run.
  • Omitting only the final commit produced no conversation item or transcription during the ten-second observation window.

Diagnostics

  • Server event IDs for investigation:
Commit acknowledgement:  event_ENekt2uYc48KZ031NycEZ
Transcription completion: event_ENeku2ruuiqKJq39ebvaa
Successful retrieval A:   event_ENekuYhv69bCIGd6Gh1Ma
Failed retrieval B:       event_ENekusbFjZymJqa0KPxWB
  • When: September 13, 2026, Asia/Tokyo (UTC+09:00).
  • Frequency: Reproduced in multiple replay tests containing cancellation. An occurrence rate has not been established.
  • Logging: Incoming IDs were logged directly from parsed WebSocket messages before application transcript processing. The failing replay log is summarized and omits transcription deltas, so it does not establish B’s first appearance on the connection.

The response.cancel documentation says cancelling when no response is active is safe and leaves the session unaffected. Could you investigate why the transcription event references an item that cannot be retrieved, and whether cancellation immediately before committing active speech can trigger this inconsistency?