{"record":{"id":"19f3b870b3e5e07f","repo":"BerriAI/litellm","slug":"invalid-json-message-message-str","errorCode":null,"errorMessage":"Invalid JSON message: {message_str}","messagePattern":"Invalid JSON message: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/gemini/realtime/transformation.py","lineNumber":535,"sourceCode":"        }\n\n        return [json.dumps(client_content_message)]\n\n    def transform_realtime_request(\n        self,\n        message: str,\n        model: str,\n        session_configuration_request: str | None = None,\n    ) -> list[str]:\n        realtime_input_dict: BidiGenerateContentRealtimeInput = {}\n        try:\n            json_message: Final = json.loads(message)\n        except json.JSONDecodeError:\n            if isinstance(message, bytes):\n                message_str = message.decode(\"utf-8\", errors=\"replace\")\n            else:\n                message_str = str(message)\n            raise ValueError(f\"Invalid JSON message: {message_str}\")\n\n        messages: Final[list[str]] = []\n        msg_type: Final = json_message.get(\"type\")\n\n        if msg_type == \"session.update\":\n            return self._handle_session_update(json_message, model, session_configuration_request)\n\n        if msg_type == \"response.create\":\n            return []  # Gemini responds automatically; nothing to forward\n\n        if msg_type == \"conversation.item.create\":\n            return self._handle_conversation_item(json_message)\n\n        if msg_type == \"input_audio_buffer.append\":\n            realtime_input_dict[\"audio\"] = HttpxBlobType(\n                mimeType=self.get_audio_mime_type(), data=json_message[\"audio\"]\n            )\n","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/gemini/realtime/transformation.py#L517-L553","documentation":"Raised in transform_realtime_input when a client→Gemini message cannot be parsed with json.loads. Before anything is forwarded over the WebSocket, each inbound message must be a JSON object whose 'type' field routes it (session.update, response.create, conversation.item.create, ...). Non-JSON input (plain text, binary audio frames handed to the wrong API) fails fast with the offending string echoed.","triggerScenarios":"Calling the realtime input transform with a plain-text user message, a binary audio buffer that should have gone to the dedicated audio path, or malformed/truncated JSON from a custom client.","commonSituations":"Porting an OpenAI realtime client that sends audio as base64 strings in the wrong envelope; custom WebSocket frontends emitting debug strings; concatenation bugs producing invalid JSON.","solutions":["Ensure every client message is a serialized JSON object with a 'type' field before calling transform_realtime_input.","Send raw audio through the audio-input path (base64 realtimeInput), not the JSON transform path.","Validate inbound frames with json.loads in your client before forwarding."],"exampleFix":"# before\nmsgs = config.transform_realtime_input(message='hello world', model=model)\n\n# after\nimport json\nframe = json.dumps({'type': 'conversation.item.create', 'item': {'type': 'message', 'role': 'user', 'content': [{'type': 'input_text', 'text': 'hello world'}]}})\nmsgs = config.transform_realtime_input(message=frame, model=model)\n","handlingStrategy":"validation","validationCode":"import json\n\ndef is_valid_client_frame(message) -> bool:\n    try:\n        parsed = json.loads(message)\n    except (json.JSONDecodeError, TypeError, UnicodeDecodeError):\n        return False\n    return isinstance(parsed, dict) and \"type\" in parsed","typeGuard":"import json\n\ndef is_json_object_with_type(message: str | bytes) -> bool:\n    try:\n        parsed = json.loads(message)\n    except (json.JSONDecodeError, TypeError):\n        return False\n    return isinstance(parsed, dict) and isinstance(parsed.get(\"type\"), str)","tryCatchPattern":"try:\n    msgs = config.transform_realtime_input(message=message, model=model)\nexcept ValueError as e:\n    if \"Invalid JSON message\" in str(e):\n        logger.warning(\"Rejecting non-JSON client frame\")\n        return []\n    raise","preventionTips":["json.dumps every client frame before sending; never send raw strings.","Send audio via the base64 audio envelope, not the JSON transform path.","Validate outbound frames in a client-side middleware during development."],"tags":["gemini","realtime","json-parsing","client-input","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}