BerriAI/litellm · error · ValueError
Invalid JSON message: {message_str}
Error message
Invalid JSON message: {message_str} What it means
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.
Source
Thrown at litellm/llms/gemini/realtime/transformation.py:535
}
return [json.dumps(client_content_message)]
def transform_realtime_request(
self,
message: str,
model: str,
session_configuration_request: str | None = None,
) -> list[str]:
realtime_input_dict: BidiGenerateContentRealtimeInput = {}
try:
json_message: Final = json.loads(message)
except json.JSONDecodeError:
if isinstance(message, bytes):
message_str = message.decode("utf-8", errors="replace")
else:
message_str = str(message)
raise ValueError(f"Invalid JSON message: {message_str}")
messages: Final[list[str]] = []
msg_type: Final = json_message.get("type")
if msg_type == "session.update":
return self._handle_session_update(json_message, model, session_configuration_request)
if msg_type == "response.create":
return [] # Gemini responds automatically; nothing to forward
if msg_type == "conversation.item.create":
return self._handle_conversation_item(json_message)
if msg_type == "input_audio_buffer.append":
realtime_input_dict["audio"] = HttpxBlobType(
mimeType=self.get_audio_mime_type(), data=json_message["audio"]
)
View on GitHub (pinned to 6c2dcb801b)
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.
Example fix
# before
msgs = config.transform_realtime_input(message='hello world', model=model)
# after
import json
frame = json.dumps({'type': 'conversation.item.create', 'item': {'type': 'message', 'role': 'user', 'content': [{'type': 'input_text', 'text': 'hello world'}]}})
msgs = config.transform_realtime_input(message=frame, model=model)
Defensive patterns
Strategy: validation
Validate before calling
import json
def is_valid_client_frame(message) -> bool:
try:
parsed = json.loads(message)
except (json.JSONDecodeError, TypeError, UnicodeDecodeError):
return False
return isinstance(parsed, dict) and "type" in parsed Type guard
import json
def is_json_object_with_type(message: str | bytes) -> bool:
try:
parsed = json.loads(message)
except (json.JSONDecodeError, TypeError):
return False
return isinstance(parsed, dict) and isinstance(parsed.get("type"), str) Try / catch
try:
msgs = config.transform_realtime_input(message=message, model=model)
except ValueError as e:
if "Invalid JSON message" in str(e):
logger.warning("Rejecting non-JSON client frame")
return []
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- File data is required
- api_key is required for Gemini API calls
- Unexpected part type: {part}
- Unexpected model turn event, no 'parts' key: {model_turn}
- Unexpected delta type: {delta_type}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/19f3b870b3e5e07f.
Report an issue: GitHub.