6.7 KiB
RealtimeKit Gotchas & Troubleshooting
Common Errors
"Cannot connect to meeting"
Cause: Auth token invalid/expired, API credentials lack permissions, or network blocks WebRTC Solution: Verify token validity, check API token has Realtime / Realtime Admin permissions, enable TURN service for restrictive networks
"No video/audio tracks"
Cause: Browser permissions not granted, video/audio not enabled, device in use, or device unavailable
Solution:
Request browser permissions explicitly, verify initialization config, use meeting.self.getAllDevices() to debug, close other apps using device
"Participant count mismatched"
Cause: meeting.participants doesn't include meeting.self
Solution: Total count = meeting.participants.joined.size() + 1
"Events not firing"
Cause: Listeners registered after actions, incorrect event name, or wrong namespace
Solution:
Register listeners before calling meeting.join(), check event names against docs, verify correct namespace
"CORS errors in API calls"
Cause: Making REST API calls from client-side Solution: All REST API calls must be server-side (Workers, backend). Never expose API tokens to clients.
"Preset not applying"
Cause: Preset doesn't exist, name mismatch (case-sensitive), or participant created before preset Solution: Verify preset exists via Dashboard or API, check exact spelling and case, create preset before adding participants
"Token reuse error"
Cause: Reusing participant tokens across sessions Solution: Generate fresh token per session. Use refresh endpoint if token expires during session.
"Video quality poor"
Cause: Insufficient bandwidth, resolution/bitrate too high, or CPU overload
Solution:
Lower mediaConfiguration.video resolution/frameRate, monitor network conditions, reduce participant count or grid size
"Echo or audio feedback"
Cause: Multiple devices picking up same audio source Solution:
- Lower
mediaConfiguration.videoresolution/frameRate - Monitor network conditions
- Reduce participant count or grid size
Issue: Echo or audio feedback
Cause: Multiple devices picking up same audio source
Solutions:
Enable echoCancellation: true in mediaConfiguration.audio, use headphones, mute when not speaking
"Screen share not working"
Cause: Browser doesn't support screen sharing API, permission denied, or wrong displaySurface config
Solution:
Use Chrome/Edge/Firefox (Safari limited support), check browser permissions, try different displaySurface values ('window', 'monitor', 'browser')
"How do I schedule meetings?"
Cause: RealtimeKit has no built-in scheduling system Solution: Store meeting IDs in your database with timestamps. Generate participant tokens only when user should join. Example:
// Store in DB
{ meetingId: 'abc123', scheduledFor: '2026-02-15T10:00:00Z', userId: 'user456' }
// Generate token when user clicks "Join" near scheduled time
const response = await fetch('/api/join-meeting', {
method: 'POST',
body: JSON.stringify({ meetingId: 'abc123' })
});
const { authToken } = await response.json();
"Recording not starting"
Cause: Preset lacks recording permissions, no active session, or API call from client
Solution:
Verify preset has canRecord: true and canStartStopRecording: true, ensure session is active (at least one participant), make recording API calls server-side only
Limits
| Resource | Limit |
|---|---|
| Max participants per session | 100 |
| Max concurrent sessions per App | 1000 |
| Max recording duration | 6 hours |
| Max meeting duration | 24 hours |
| Max chat message length | 4000 characters |
| Max preset name length | 64 characters |
| Max meeting title length | 256 characters |
| Max participant name length | 256 characters |
| Token expiration | 24 hours (default) |
| WebRTC ports required | UDP 1024-65535 |
Network Requirements
Firewall Rules
Allow outbound UDP/TCP to:
*.cloudflare.comports 443, 80- UDP ports 1024-65535 (WebRTC media)
TURN Service
Enable for users behind restrictive firewalls/proxies:
// wrangler.jsonc
{
"vars": {
"TURN_SERVICE_ID": "your_turn_service_id"
}
// Set secret: wrangler secret put TURN_SERVICE_TOKEN
}
TURN automatically configured in SDK when enabled in account.
Debugging Tips
// Check devices
const devices = await meeting.self.getAllDevices();
meeting.self.on('deviceListUpdate', ({ added, removed, devices }) => console.log('Devices:', { added, removed, devices }));
// Monitor participants
meeting.participants.joined.on('participantJoined', (p) => console.log(`${p.name} joined:`, { id: p.id, userId: p.userId, audioEnabled: p.audioEnabled, videoEnabled: p.videoEnabled }));
// Check room state
meeting.self.on('roomJoined', () => console.log('Room:', { meetingId: meeting.meta.meetingId, meetingTitle: meeting.meta.meetingTitle, participantCount: meeting.participants.joined.size() + 1, audioEnabled: meeting.self.audioEnabled, videoEnabled: meeting.self.videoEnabled }));
// Log all events
['roomJoined', 'audioUpdate', 'videoUpdate', 'screenShareUpdate', 'deviceUpdate', 'deviceListUpdate'].forEach(event => meeting.self.on(event, (data) => console.log(`[self] ${event}:`, data)));
['participantJoined', 'participantLeft'].forEach(event => meeting.participants.joined.on(event, (data) => console.log(`[participants] ${event}:`, data)));
meeting.chat.on('chatUpdate', (data) => console.log('[chat] chatUpdate:', data));
Security & Performance
Security: Do NOT
- Expose
CLOUDFLARE_API_TOKENin client code, hardcode credentials in frontend - Reuse participant tokens, store tokens in localStorage without encryption
- Allow client-side meeting creation
Security: DO
- Generate tokens server-side only, use HTTPS, implement rate limiting
- Validate user auth before generating tokens, use
custom_participant_idto map to your user system - Set appropriate preset permissions per user role, rotate API tokens regularly
Performance
- CPU: Lower video resolution/frameRate, disable video for audio-only, use
meeting.participants.activefor large meetings, implement virtual scrolling - Bandwidth: Set max resolution in
mediaConfiguration, disable screenshare audio if unneeded, use audio-only mode, implement adaptive bitrate - Memory: Clean up event listeners on unmount, call
meeting.leave()when done, don't store large participant arrays
In This Reference
- README.md - Overview, core concepts, quick start
- configuration.md - SDK config, presets, wrangler setup
- api.md - Client SDK APIs, REST endpoints
- patterns.md - Common patterns, React hooks, backend integration