Merge pull request #4358 from pipecat-ai/filipi/fix_aiortc_sctp

Fixed SmallWebRTC data channel silently stalling on networks with a 1280-byte MTU
This commit is contained in:
Filipi da Silva Fuchter
2026-04-23 17:49:18 -03:00
committed by GitHub
3 changed files with 38 additions and 1 deletions

1
changelog/4358.fixed.md Normal file
View File

@@ -0,0 +1 @@
- Fixed SmallWebRTC data channel silently stalling on networks with a 1280-byte MTU (IPv6, Tailscale overlays, many consumer VPNs). aiortc's default SCTP chunk size of 1200 bytes produces ~1305-byte UDP datagrams after headers, which the kernel rejects with EMSGSIZE; aiortc has no path-MTU discovery so it retransmits forever at the same oversized size. The chunk size is now clamped to 1100 bytes (~1205-byte datagrams, ~75 bytes of slack). Override with `PIPECAT_SCTP_MAX_CHUNK_SIZE` if your path MTU requires a different value.

View File

@@ -215,3 +215,9 @@ WHATSAPP_APP_SECRET=...
# xAI / Grok # xAI / Grok
XAI_API_KEY=... XAI_API_KEY=...
# PIPECAT_SCTP_MAX_CHUNK_SIZE controls the maximum SCTP DATA-chunk payload
# size (bytes) used by aiortc's data channel. The default is 1100.
# All the details here:
# https://docs.pipecat.ai/api-reference/server/services/transport/small-webrtc#pipecat_sctp_max_chunk_size
#PIPECAT_SCTP_MAX_CHUNK_SIZE=1100

View File

@@ -13,6 +13,7 @@ for real-time communication applications.
import asyncio import asyncio
import json import json
import os
import time import time
import uuid import uuid
from typing import Any, Literal from typing import Any, Literal
@@ -23,6 +24,7 @@ from pydantic import BaseModel, TypeAdapter
from pipecat.utils.base_object import BaseObject from pipecat.utils.base_object import BaseObject
try: try:
import aiortc.rtcsctptransport as _sctp_transport
from aiortc import ( from aiortc import (
RTCConfiguration, RTCConfiguration,
RTCIceServer, RTCIceServer,
@@ -36,6 +38,34 @@ except ModuleNotFoundError as e:
logger.error("In order to use the SmallWebRTC, you need to `pip install pipecat-ai[webrtc]`.") logger.error("In order to use the SmallWebRTC, you need to `pip install pipecat-ai[webrtc]`.")
raise Exception(f"Missing module: {e}") raise Exception(f"Missing module: {e}")
# Clamp aiortc's SCTP DATA-chunk payload size so the on-wire UDP packet fits
# inside the smallest-MTU path we're likely to see (IPv6 minimum 1280,
# Tailscale overlays default to 1280, some consumer VPNs are lower).
#
# aiortc hardcodes USERDATA_MAX_LENGTH = 1200. After adding SCTP (28) +
# DTLS/GCM (~29) + UDP (8) + IPv6 (40) headers that produces a ~1305-byte
# UDP datagram — over the 1280 MTU. The kernel rejects it with EMSGSIZE,
# SCTP retransmits at the same size, and the data channel silently stalls.
# aiortc has no PMTU discovery (RFC 8831 §6), so there is no auto-recovery.
#
# 1100 brings the worst-case datagram to ~1205 bytes (~75 bytes of slack).
# Throughput cost is negligible: RTVI control frames fragment across one
# extra chunk at most, and audio uses RTP (a separate path).
#
# There is no public API to set this — RTCConfiguration exposes no MTU knob
# and all method references are bare module-global lookups, so patching the
# module attribute before any RTCSctpTransport is instantiated is the only
# option short of forking aiortc.
#
# Remove once aiortc ships DPLPMTUD (RFC 8899) or exposes this as a
# configurable parameter.
_SCTP_MAX_CHUNK_SIZE_DEFAULT = 1100
_sctp_max_chunk_size = int(
os.environ.get("PIPECAT_SCTP_MAX_CHUNK_SIZE", _SCTP_MAX_CHUNK_SIZE_DEFAULT)
)
_sctp_transport.USERDATA_MAX_LENGTH = _sctp_max_chunk_size
logger.debug(f"[SCTP] USERDATA_MAX_LENGTH set to {_sctp_max_chunk_size}")
SIGNALLING_TYPE = "signalling" SIGNALLING_TYPE = "signalling"
AUDIO_TRANSCEIVER_INDEX = 0 AUDIO_TRANSCEIVER_INDEX = 0
VIDEO_TRANSCEIVER_INDEX = 1 VIDEO_TRANSCEIVER_INDEX = 1