{"record":{"id":"147e349063d983c1","repo":"microsoft/semantic-kernel","slug":"failed-to-receive-audio-frame","errorCode":null,"errorMessage":"Failed to receive audio frame","messagePattern":"Failed to receive audio frame","errorType":"exception","errorClass":"MediaStreamError","httpStatus":null,"severity":"error","filePath":"python/samples/concepts/realtime/utils.py","lineNumber":104,"sourceCode":"            \"channels\": channels,\n            \"frame_duration\": frame_duration,\n            \"dtype\": dtype,\n            \"frame_size\": int(sample_rate * frame_duration / 1000),\n        })\n        MediaStreamTrack.__init__(self)\n\n    async def recv(self) -> Frame:\n        \"\"\"Receive the next frame of audio data.\"\"\"\n        if not self._recording_task:\n            self._recording_task = asyncio.create_task(self.start_recording())\n\n        try:\n            frame = await self._queue.get()\n            self._queue.task_done()\n            return frame\n        except Exception as e:\n            logger.error(f\"Error receiving audio frame: {e!s}\")\n            raise MediaStreamError(\"Failed to receive audio frame\")\n\n    def _sounddevice_callback(self, indata: np.ndarray, frames: int, time: Any, status: Any) -> None:\n        if status:\n            logger.warning(f\"Audio input status: {status}\")\n        if self._loop and self._loop.is_running():\n            asyncio.run_coroutine_threadsafe(self._queue.put(self._create_frame(indata)), self._loop)\n\n    def _create_frame(self, indata: np.ndarray) -> Frame:\n        audio_data = indata.copy()\n        if audio_data.dtype != self.dtype:\n            audio_data = (\n                (audio_data * 32767).astype(self.dtype) if self.dtype == np.int16 else audio_data.astype(self.dtype)\n            )\n        frame = AudioFrame(\n            format=\"s16\",\n            layout=\"mono\",\n            samples=len(audio_data),\n        )","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/samples/concepts/realtime/utils.py#L86-L122","documentation":"MediaStreamError raised by the realtime sample's audio track recv(): the await on the internal asyncio queue (self._queue.get()) is wrapped in try/except, and any Exception during dequeue is logged then re-raised as MediaStreamError('Failed to receive audio frame'). It is a sample-level wrapper around aiortc's MediaStreamTrack meant to surface audio-capture failures to the WebRTC peer consistently.","triggerScenarios":"The queue.get() / task_done() call raises - e.g. the recording task was cancelled, the event loop is closed, the queue is in a bad state, or the underlying sounddevice callback stopped feeding frames (device disconnected, permission revoked, loop not running).","commonSituations":"Audio input device removed/disconnected mid-session; microphone permission denied by the OS; the asyncio loop (self._loop) stopped so run_coroutine_threadsafe callbacks no longer enqueue frames; or the track's recording task was cancelled during teardown.","solutions":["Confirm the microphone device id passed to the recorder exists and is permitted (check OS audio permissions).","Ensure the asyncio event loop is running for the lifetime of the track so _sounddevice_callback can enqueue frames.","Catch MediaStreamError at the consumer and end the WebRTC track cleanly rather than crashing.","Verify sounddevice can open the stream at the configured sample_rate/channels/dtype before starting."],"exampleFix":"# before\ntry:\n    frame = await self._queue.get()\n    self._queue.task_done()\n    return frame\nexcept Exception as e:\n    logger.error(f\"Error receiving audio frame: {e!s}\")\n    raise MediaStreamError(\"Failed to receive audio frame\")\n# after - distinguish cancellation from real errors, preserve cause\ntry:\n    frame = await self._queue.get()\n    self._queue.task_done()\n    return frame\nexcept asyncio.CancelledError:\n    raise\nexcept Exception as e:\n    logger.error(f\"Error receiving audio frame: {e!s}\")\n    raise MediaStreamError(\"Failed to receive audio frame\") from e","handlingStrategy":"try-catch","validationCode":"# Before starting the track, confirm the device can be opened.\nimport sounddevice as sd\ndef device_available(device, sample_rate, channels, dtype):\n    try:\n        sd.check_input_settings(device=device, samplerate=sample_rate,\n                                channels=channels, dtype=dtype)\n        return True\n    except Exception:\n        return False","typeGuard":"def track_ready(track) -> bool:\n    import asyncio\n    return (\n        getattr(track, \"_loop\", None) is not None\n        and track._loop.is_running()\n        and getattr(track, \"_recording_task\", None) is not None\n        and not track._recording_task.done()\n    )","tryCatchPattern":"try:\n    frame = await track.recv()\nexcept MediaStreamError:\n    # end the WebRTC track cleanly; do not crash the peer\n    await track.stop()","preventionTips":["Verify the mic device id and OS audio permissions before starting the track.","Keep the asyncio loop running for the track's lifetime so frames keep flowing.","Catch MediaStreamError at the consumer and stop the track instead of crashing.","Preserve the original exception (raise ... from e) for diagnosability."],"tags":["python","sample","realtime","audio","webrtc","hardware"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}