{"record":{"id":"cc9c21be8d7cc34e","repo":"BerriAI/litellm","slug":"unexpected-part-type-part","errorCode":null,"errorMessage":"Unexpected part type: {part}","messagePattern":"Unexpected part type: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/gemini/realtime/transformation.py","lineNumber":164,"sourceCode":"        Map the model turn event to the OpenAI realtime events.\n\n        Returns either:\n        - response.text.delta - model_turn: {\"parts\": [{\"text\": \"...\"}]}\n        - response.audio.delta - model_turn: {\"parts\": [{\"inlineData\": {\"mimeType\": \"audio/pcm\", \"data\": \"...\"}}]}\n\n        Assumes parts is a single element list.\n        \"\"\"\n        if \"parts\" in model_turn:\n            parts: Final = model_turn[\"parts\"]\n            if len(parts) != 1:\n                verbose_logger.warning(\"Realtime: Expected 1 part, got %s for Gemini model turn event.\", len(parts))\n            part: Final = parts[0]\n            if \"text\" in part:\n                return OpenAIRealtimeEventTypes.RESPONSE_TEXT_DELTA\n            elif \"inlineData\" in part:\n                return OpenAIRealtimeEventTypes.RESPONSE_AUDIO_DELTA\n            else:\n                raise ValueError(f\"Unexpected part type: {part}\")\n        raise ValueError(f\"Unexpected model turn event, no 'parts' key: {model_turn}\")\n\n    def map_generation_complete_event(self, delta_type: ALL_DELTA_TYPES | None) -> OpenAIRealtimeEventTypes:\n        if delta_type == \"text\":\n            return OpenAIRealtimeEventTypes.RESPONSE_TEXT_DONE\n        elif delta_type == \"audio\":\n            return OpenAIRealtimeEventTypes.RESPONSE_AUDIO_DONE\n        else:\n            raise ValueError(f\"Unexpected delta type: {delta_type}\")\n\n    def get_audio_mime_type(self, input_audio_format: str = \"pcm16\"):\n        mime_types: Final = {\n            \"pcm16\": \"audio/pcm;rate=24000\",\n            \"g711_ulaw\": \"audio/pcmu\",\n            \"g711_alaw\": \"audio/pcma\",\n        }\n\n        return mime_types.get(input_audio_format, \"application/octet-stream\")","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/gemini/realtime/transformation.py#L146-L182","documentation":"Raised in map_model_turn_event when a Gemini realtime serverContent modelTurn part contains neither a 'text' key nor an 'inlineData' key. The transformer only knows how to map text and inline-audio parts to OpenAI realtime events, so any other part shape (e.g. functionCall, videoMetadata, or a new API part type) is rejected with ValueError echoing the offending part.","triggerScenarios":"Gemini realtime server sends a modelTurn part of a new type (function call part, tool output, future part kinds) that this litellm version's mapping does not cover; typically after Google ships new realtime capabilities.","commonSituations":"Upgrading the Gemini model version mid-session; litellm version lagging behind the Gemini realtime API; enabling tools/modalities the transformer doesn't handle.","solutions":["Upgrade litellm to the latest version — new Gemini part types get mapping support over time.","Restrict the realtime session config (modalities/tools) so the model only emits text/audio parts.","If persisting, catch the ValueError per-frame so one bad part doesn't kill the whole session."],"exampleFix":"# before\nevent = config.map_model_turn_event(model_turn)\n\n# after\ntry:\n    event = config.map_model_turn_event(model_turn)\nexcept ValueError:\n    logger.warning('Skipping unsupported Gemini modelTurn part: %r', model_turn)\n    return None  # drop the frame, keep the session alive\n","handlingStrategy":"try-catch","validationCode":"def is_supported_model_turn(turn: dict) -> bool:\n    parts = turn.get(\"parts\") if isinstance(turn, dict) else None\n    return bool(parts) and all(\n        isinstance(p, dict) and (\"text\" in p or \"inlineData\" in p) for p in parts\n    )","typeGuard":"def is_supported_model_turn(turn: dict) -> bool:\n    if not isinstance(turn, dict) or \"parts\" not in turn:\n        return False\n    return all(\n        isinstance(p, dict) and (\"text\" in p or \"inlineData\" in p)\n        for p in turn[\"parts\"]\n    )","tryCatchPattern":"try:\n    event = config.map_model_turn_event(model_turn)\nexcept ValueError as e:\n    if \"Unexpected part type\" in str(e):\n        logger.warning(\"Skipping unsupported part: %s\", e)\n        continue\n    raise","preventionTips":["Constrain session modalities to text/audio so unsupported part types are not produced.","Keep litellm current with Gemini realtime API changes.","Wrap per-frame mapping so one bad part never kills a live session."],"tags":["gemini","realtime","parsing","event-mapping","compatibility"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}