mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-20 18:11:27 -07:00
4.8 KiB
4.8 KiB
Gotchas & Troubleshooting
Common Errors
"Slow initial connect (~1.8s)"
Cause: First STUN delayed during consensus forming (normal behavior) Solution: Subsequent connections are faster. CF detects DTLS ClientHello early to compensate.
"No media flow"
Cause: SDP exchange incomplete, connection not established, tracks not added before offer, browser permissions missing Solution:
- Verify SDP exchange complete
- Check
pc.connectionState === 'connected' - Ensure tracks added before creating offer
- Confirm browser permissions granted
- Use
chrome://webrtc-internalsfor debugging
"Track not receiving"
Cause: Track not published, track ID not shared, session IDs mismatch, pc.ontrack not set, renegotiation needed
Solution:
- Verify track published successfully
- Confirm track ID shared between peers
- Check session IDs match
- Set
pc.ontrackhandler before answer - Trigger renegotiation if needed
"ICE connection failed"
Cause: Network changed, firewall blocked UDP, TURN needed, transient network issue Solution:
pc.oniceconnectionstatechange = async () => {
if (pc.iceConnectionState === 'failed') {
console.warn('ICE failed, attempting restart');
await pc.restartIce(); // Triggers new ICE gathering
// Create new offer with ICE restart flag
const offer = await pc.createOffer({iceRestart: true});
await pc.setLocalDescription(offer);
// Send to backend → Cloudflare API
await fetch(`/api/sessions/${sessionId}/renegotiate`, {
method: 'PUT',
body: JSON.stringify({sdp: offer.sdp})
});
}
};
"Track stuck/frozen"
Cause: Sender paused track, network congestion, codec mismatch, mobile browser backgrounded Solution:
- Check
track.enabledandtrack.readyState === 'live' - Verify sender active:
pc.getSenders().find(s => s.track === track) - Check stats for packet loss/jitter (see patterns.md)
- On mobile: Re-acquire tracks when app foregrounded
- Test with different codecs if persistent
"Network change disconnects call"
Cause: Mobile switching WiFi↔cellular, laptop changing networks Solution:
// Listen for network changes
if ('connection' in navigator) {
(navigator as any).connection.addEventListener('change', async () => {
console.log('Network changed');
await pc.restartIce(); // Use ICE restart pattern above
});
}
// Or use PartyTracks (handles automatically)
Retry with Exponential Backoff
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const res = await fetch(url, options);
if (res.ok) return res;
if (res.status >= 500) throw new Error('Server error');
return res; // Client error, don't retry
} catch (err) {
if (i === maxRetries - 1) throw err;
const delay = Math.min(1000 * 2 ** i, 10000); // Cap at 10s
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Debugging with chrome://webrtc-internals
- Open
chrome://webrtc-internalsin Chrome/Edge - Find your PeerConnection in the list
- Check Stats graphs for packet loss, jitter, bandwidth
- Check ICE candidate pairs: Look for
succeededstate, relay vs host candidates - Check getStats: Raw metrics for inbound/outbound RTP
- Look for errors in Event log:
iceConnectionState,connectionStatechanges - Export data with "Download the PeerConnection updates and stats data" button
- Common issues visible here: ICE failures, high packet loss, bitrate drops
Limits
| Resource/Limit | Value | Notes |
|---|---|---|
| Egress (Free) | 1TB/month | Per account |
| Egress (Paid) | $0.05/GB | After free tier |
| Inbound traffic | Free | All plans |
| TURN service | Free | Included with SFU |
| Participants | No hard limit | Client bandwidth/CPU bound (typically 10-50 tracks) |
| Tracks per session | No hard limit | Client resources limited |
| Session duration | No hard limit | Production calls run for hours |
| WebRTC ports | UDP 1024-65535 | Outbound only, required for media |
| API rate limit | 600 req/min | Per app, burst allowed |
Security Checklist
- ✅ Never expose
CALLS_APP_SECRETto client - ✅ Validate user identity in backend before creating sessions
- ✅ Implement auth tokens for session access (JWT in custom header)
- ✅ Rate limit session creation endpoints
- ✅ Expire sessions server-side after inactivity
- ✅ Validate track IDs before subscribing (prevent unauthorized access)
- ✅ Use HTTPS for all signaling (API calls)
- ✅ Enable DTLS-SRTP (automatic with Cloudflare, encrypts media)
- ⚠️ Consider E2EE for sensitive content (implement client-side with Insertable Streams API)