#5816·pipecat

Twilio/Telnyx/Exotel serializers crash the whole call on a malformed WebSocket message

Author: wankhede04Created Sep 17, 2026Updated Sep 18, 2026

pipecat version

1.9.1.dev318 (built from main)

Python version

3.12.14

Operating System

macOS 15 (not OS-specific — this is a pure message-parsing bug)

Issue description

TwilioFrameSerializer, TelnyxFrameSerializer, and ExotelFrameSerializer's deserialize() parse incoming WebSocket data with a bare json.loads(data) and then index into the result with message["event"] / message["media"]["payload"] — no guard against malformed JSON or an unexpected message shape. Both WebSocket transports (transports/websocket/fastapi.py and .../server.py) wrap their entire receive loop in a single try/except Exception. Any exception raised inside deserialize() breaks the loop, logs "exception receiving data", and falls through to if not self._client.is_closing: trigger_client_disconnected() — i.e. a single malformed frame is treated exactly like the caller hanging up. Plivo, Vonage, and Genesys already guard against this (try/except json.JSONDecodeError plus .get() chains instead of bracket indexing) — Twilio, Telnyx, and Exotel are the three siblings that don't.

Reproduction steps

  1. Start a bot on any of the three affected telephony transports (Twilio, Telnyx, or Exotel).
  2. Have the carrier (or a proxy/test harness) send one malformed or unexpectedly-shaped WebSocket text frame — e.g. non-JSON, or a well-formed {"event": "media"} with no media key.
  3. Observe the call end immediately, as if the caller had hung up.

Minimal reproducible example

python
# Isolates the exact code path in TwilioFrameSerializer.deserialize (identical shape in
# TelnyxFrameSerializer and ExotelFrameSerializer) — no live call or WebSocket needed.
import json
message = json.loads("not json")  # raises json.decoder.JSONDecodeError
# A well-formed but unexpectedly-shaped "media" event has the same effect:
message = {"event": "media", "media": {}}
payload_base64 = message["media"]["payload"]  # raises KeyError: 'payload'

Eval scenario or recordings (optional)

No response

Expected behavior

A malformed or unexpectedly-shaped message should be logged and ignored, the same way Plivo, Vonage, and Genesys already handle it — not propagate an exception that tears down the call.

Actual behavior

deserialize() raises JSONDecodeError/KeyError, which the transport's receive loop treats as a client disconnect, silently ending the call.

Logs

bash
Traceback (most recent call last):
  File "<string>", line 2, in <module>
    message = json.loads("not json")
              ^^^^^^^^^^^^^^^^^^^^^^^
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Before you submit

  • I have personally reproduced this issue on the latest Pipecat release (or main).
  • I have searched existing issues for this bug.
  • If AI tools helped write this report, I have verified the behavior myself and will personally answer follow-up questions.