steep-mechanic-38687
02/26/2026, 10:06 PMtall-airline-16756
02/26/2026, 10:06 PMtall-airline-16756
02/26/2026, 10:06 PMsteep-mechanic-38687
02/26/2026, 10:11 PMtall-airline-16756
02/26/2026, 10:12 PMparticipant_disconnect event.
Let’s narrow this down and get you a path to a fix. Below are the most likely causes and targeted checks you can run now. After you try these, please share the results and I’ll help interpret them and suggest next steps.
Likely causes and checks
• STATE_MISMATCH or other server-side disconnect reason
◦ Check the disconnect reason attached to the participant_disconnect event (if available). If you see STATE_MISMATCH, that usually means the server stopped seeing the expected keep‑alives or the client disconnected uncleanly.
• Network / media path failure
◦ Inspect ICE / connection state for the SIP leg. If ICE keep‑alives stop for ~20s the server may assume the media path is dead and disconnect.
◦ Look for transient packet loss, NAT timeouts, or firewall behavior that could interrupt SIP or RTP.
• SIP lifecycle or API actions
◦ Verify no server-side logic is deleting the room or calling removeParticipant() around the same time (a room delete will produce ROOM_DELETED disconnects).
◦ Check any automated workflows, call-transfer logic, or SIP trunk dispatch rules that might complete or terminate the call.
• SIP trunk / gateway behavior
◦ Confirm the SIP trunk or PSTN gateway isn’t sending a BYE or disconnecting due to SIP timers, auth, or codec negotiation issues.
◦ Review SIP signaling logs (INVITE/200/ACK/BYE) for unexpected call teardown.
• Server-side events and resource issues
◦ Look for correlated server logs like node migrations, restarts, or resource limits that coincide with the disconnect times.
• Webhooks & event timing
◦ Correlate the participant_disconnect webhook timestamp with SIP signaling logs and any room events (e.g., room_finished, track updates).
Actionable steps to gather diagnostic info
1. Capture the disconnect reason from your webhook payload for the affected events and paste it here.
2. Share timestamps (UTC) for a few recent incidents so we can correlate SIP logs and room events.
3. Provide SIP signaling logs (INVITE→BYE) or the SIP response codes around the disconnect time.
4. Check and report:
◦ Were the calls inbound (user called in) or outbound (CreateSIPParticipant)?
◦ Whether disconnects happen for specific carriers, regions, or phone numbers.
◦ Any recent changes to SIP trunk configuration, dispatch rules, or server deployment.
1. If possible, enable more verbose logging for a short window on your SIP gateway and LiveKit server and collect:
◦ Webhook payloads for participant events
◦ SIP signaling traces (call IDs)
◦ ICE / peer connection state transitions
What I can do once you share the above
• Help interpret the disconnect reason and correlate it with SIP signaling and room events.
• Recommend specific config changes (SIP timers, ICE settings, retry logic) based on the root cause.
• Provide example code for safer reconnect handling (e.g., re-dial via CreateSIPParticipant) if the disconnects are expected and you want automatic recovery.
If you want to start with the easiest checks, please share the disconnect reason from one of the webhook payloads and a timestamp of the event — I’ll walk through the next diagnostic steps with you.
Sources: Understanding the STATE_MISMATCH Disconnect Reason | Understanding the STATE_MISMATCH Disconnect Reason | LiveKit | SIP Lifecycle Management Agent | LiveKit Documentation | Server-side | livekit/livekit_composite | DeepWiki | server-sdk-ruby | livekit/livekit_composite | DeepWikisteep-mechanic-38687
02/26/2026, 10:32 PMtall-airline-16756
02/26/2026, 10:33 PMtwilio.call_sid. Use that value to look up the Twilio call.
Why this works:
• CallSid is unique per Twilio call and persists through Twilio’s logs/webhooks, so mapping it into LiveKit provides a one-to-one correlation.
2) Use SIP Call-ID
• SIP sessions carry a Call‑ID header which LiveKit surfaces (e.g., sip.callID on the participant).
• If Twilio exposes or logs the same Call‑ID for the SIP leg, you can match on that value between systems.
3) Phone number + timestamp (fallback)
• If you can’t pass headers, match LiveKit’s sip.phoneNumber and the call timestamp to Twilio’s call logs.
• This is less robust (possible collisions) but often works when traffic is low or timestamps are precise.
Practical steps to implement the header approach
1. Configure Twilio to add a SIP header with CallSid:
◦ Add X-CallSid: {CallSid} (naming your header consistently).
1. Configure LiveKit inbound trunk to map headers to attributes:
◦ Use headers_to_attributes so the header becomes, for example, twilio.call_sid on the participant.
1. Log or store the participant attribute at join time:
◦ Capture it from the webhook or via the participant object in your application.
1. Correlate in your dashboard/ELK/DB:
◦ Join LiveKit events (participant join/leave/webhooks) and Twilio webhooks/logs by twilio.call_sid.
What to check if something’s not matching
• Ensure Twilio actually sends the header in the INVITE (inspect SIP traces for X-CallSid).
• Verify LiveKit’s header mapping is configured correctly and that participant attributes are populated.
• Confirm you’re reading the attribute from the right place (participant attributes vs. other metadata).
• If using Call‑ID, confirm Twilio and LiveKit show the same Call‑ID value in their logs.
If you’d like, tell me:
• Are your calls inbound (PSTN → Twilio → LiveKit) or outbound from LiveKit to Twilio?
• Do you already have any headers being sent (e.g., X-CallSid) or do you want example TwiML/SIP configuration to add them?
• I can provide a small example of the header mapping configuration and the code snippet you’d use to read the attribute in your app. Sources: How to do call transfers with Five9 | LiveKit | SIP Lifecycle Management Agent | LiveKit Documentationsteep-mechanic-38687
02/26/2026, 10:38 PMtall-airline-16756
02/26/2026, 10:39 PM// Example: createSipParticipant.js
// Install: npm install livekit-server-sdk
const { createSipParticipant } = require('livekit-server-sdk'); // adjust import if your SDK version differs
const LIVEKIT_URL = process.env.LIVEKIT_URL;
const API_KEY = process.env.LIVEKIT_API_KEY;
const API_SECRET = process.env.LIVEKIT_API_SECRET;
async function dialWithMapping() {
// unique external id you want to map (e.g., Twilio CallSid)
const externalId = 'TW_CALLSID_123456';
// Build request payload
const req = {
roomName: 'support-room-1',
participantIdentity: 'sip-agent-001',
participantName: 'Agent SIP Participant',
sipTrunkId: 'ST_abcdef', // your SIP trunk id
sipCallTo: '+15551234567', // number to dial
waitUntilAnswered: true,
playDialtone: true,
krispEnabled: false,
// 1) Participant metadata/attributes stored with the participant in LiveKit
participantMetadata: JSON.stringify({ externalId }),
// 2) Custom SIP headers included in the INVITE (Twilio will see these headers)
headers: {
'X-External-Id': externalId,
// you can include other headers as needed
},
};
// Create the SIP participant (SDK initialization pattern may vary across versions)
const resp = await createSipParticipant(LIVEKIT_URL, API_KEY, API_SECRET, req);
console.log('Create SIP Participant response:', resp);
}
dialWithMapping().catch(err => {
console.error('Failed to create SIP participant:', err);
});
Notes and recommendations
• Participant metadata: Use participantMetadata to store structured data (like CallSid) that you can read from LiveKit participant attributes or from the participant_attributes_changed webhook.
• SIP headers: Send the external ID in a custom SIP header (e.g., X-External-Id or X-CallSid) so Twilio can log it or include it in its traces/webhooks. Then map that header in your trunk configuration if you want it reflected in LiveKit attributes.
• Pick one canonical key (e.g., twilio.call_sid or externalId) so your logs and dashboards use a consistent field.
• Verify by inspecting SIP INVITE traces to ensure the header is sent, and check the participant attributes or webhook payload when the SIP participant joins.
If you tell me which server SDK version you’re using (or paste how you currently call CreateSIPParticipant), I can tailor the exact import/constructor and provide a ready-to-run snippet for that version.
Sources: server-sdk-ruby | livekit/livekit_composite | DeepWiki | How to do call transfers with Five9 | LiveKit