# 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.video` resolution/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:

```typescript
// 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.com` ports 443, 80
- UDP ports 1024-65535 (WebRTC media)

### TURN Service

Enable for users behind restrictive firewalls/proxies:

```jsonc
// 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

```typescript
// 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_TOKEN` in 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_id` to 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.active` for 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](README.md) - Overview, core concepts, quick start
- [configuration.md](configuration.md) - SDK config, presets, wrangler setup
- [api.md](api.md) - Client SDK APIs, REST endpoints
- [patterns.md](patterns.md) - Common patterns, React hooks, backend integration
