Summary
A production warm transfer on LiveKit Cloud failed before the outbound SIP INVITE because CreateSIPParticipant rejected a Twilio-issued X-Twilio-CallToken whose value exceeded 1,024 bytes.
Twilio's Immutable Call Forwarding flow requires this token to be forwarded unchanged on the outbound leg so the original caller ID can be presented. The token is opaque and may contain SHAKEN/STIR and DIV PASSporTs, so truncating it is not valid.
Observed error
TwirpError: invalid header value for X-Twilio-CallToken:
header X-Twilio-CallToken: value too long (max 1024 characters)
HTTP status: 400
Twirp code: invalid_argument
at SipClient.createSipParticipant
The token was captured from the inbound Twilio SIP INVITE and passed back unchanged. Its value is intentionally omitted because it is a bearer credential.
Reproduction
- Receive an inbound call through Twilio Elastic SIP Trunking with Immutable Call Forwarding enabled.
- Capture the inbound
X-Twilio-CallToken.
- Use the original caller number as
sip_number and pass the unchanged token to CreateSIPParticipant:
{
"sip_number": "<original caller number>",
"headers": {
"X-Twilio-CallToken": "<Twilio-issued token longer than 1024 bytes>"
}
}
- LiveKit returns
400 invalid_argument before creating the outbound SIP leg.
Expected behavior
CreateSIPParticipant should support the sizes that Twilio can legitimately issue for X-Twilio-CallToken, or LiveKit should provide a documented alternative for this carrier integration.
Possible resolutions could include:
- raising the per-header limit;
- making it configurable; or
- explicitly accommodating
X-Twilio-CallToken.
Changing the outbound trunk transport to TCP does not avoid the current failure because request validation occurs before SIP traffic is sent.
Relevant implementation
The limit is currently hard-coded in the protocol validator:
https://github.com/livekit/protocol/blob/9aa28a1171907bad7098b59546a77ebdacd8e725/livekit/sip_validation.go#L243-L256
The corresponding tests describe the length restriction as implementation-specific and not part of RFC 3261:
https://github.com/livekit/protocol/blob/9aa28a1171907bad7098b59546a77ebdacd8e725/livekit/sip_validation_test.go#L79-L113
Twilio requires the CallToken to be retransmitted unchanged and documents that it can contain SHAKEN/STIR and DIV PASSporTs:
https://www.twilio.com/docs/voice/trusted-calling-with-shakenstir#call-forwarding
Environment
- LiveKit Cloud
livekit-server-sdk: 2.15.3
@livekit/agents: 1.6.2
- Observed: 2026-08-12
We can provide sanitized trace timing and Cloud job/room identifiers privately if useful.
Summary
A production warm transfer on LiveKit Cloud failed before the outbound SIP INVITE because
CreateSIPParticipantrejected a Twilio-issuedX-Twilio-CallTokenwhose value exceeded 1,024 bytes.Twilio's Immutable Call Forwarding flow requires this token to be forwarded unchanged on the outbound leg so the original caller ID can be presented. The token is opaque and may contain SHAKEN/STIR and DIV PASSporTs, so truncating it is not valid.
Observed error
The token was captured from the inbound Twilio SIP INVITE and passed back unchanged. Its value is intentionally omitted because it is a bearer credential.
Reproduction
X-Twilio-CallToken.sip_numberand pass the unchanged token toCreateSIPParticipant:{ "sip_number": "<original caller number>", "headers": { "X-Twilio-CallToken": "<Twilio-issued token longer than 1024 bytes>" } }400 invalid_argumentbefore creating the outbound SIP leg.Expected behavior
CreateSIPParticipantshould support the sizes that Twilio can legitimately issue forX-Twilio-CallToken, or LiveKit should provide a documented alternative for this carrier integration.Possible resolutions could include:
X-Twilio-CallToken.Changing the outbound trunk transport to TCP does not avoid the current failure because request validation occurs before SIP traffic is sent.
Relevant implementation
The limit is currently hard-coded in the protocol validator:
https://github.com/livekit/protocol/blob/9aa28a1171907bad7098b59546a77ebdacd8e725/livekit/sip_validation.go#L243-L256
The corresponding tests describe the length restriction as implementation-specific and not part of RFC 3261:
https://github.com/livekit/protocol/blob/9aa28a1171907bad7098b59546a77ebdacd8e725/livekit/sip_validation_test.go#L79-L113
Twilio requires the CallToken to be retransmitted unchanged and documents that it can contain SHAKEN/STIR and DIV PASSporTs:
https://www.twilio.com/docs/voice/trusted-calling-with-shakenstir#call-forwarding
Environment
livekit-server-sdk: 2.15.3@livekit/agents: 1.6.2We can provide sanitized trace timing and Cloud job/room identifiers privately if useful.