Troubleshooting Guide
Quick Diagnostic Checklist
- Session server running? (
curl http://localhost:3000/health) - Plugin installed? (Check Dashboard > Plugins)
- Client injection ready in the OpenWatchParty plugin dashboard? (Custom HTML is only a fallback.)
- Browser cache cleared? (Ctrl+F5)
- Correct WebSocket URL?
- Reverse proxy routes
/wsto the session server, and the server port is reachable only from trusted hosts?
Related documentation:
- Architecture - Edge Cases - Expected behavior in various scenarios
- Security Guide - Authentication and CORS issues
- Configuration - Server and plugin settings
Common Issues
Watch Party Button Not Visible
Symptoms:
- No Watch Party button (a screen with two viewers) in the video player header
- Panel doesn’t appear
Solutions:
- Check the OpenWatchParty plugin dashboard
- Go to Dashboard > Plugins > OpenWatchParty and run diagnostics.
- Verify client injection reports ready. Native injection does not need a Custom HTML entry.
- If native injection is blocked, follow Client Injection Compatibility Fallbacks. For the Custom HTML fallback, add this line under Dashboard > General > Branding:
<script src="/OpenWatchParty/ClientScript"></script>
- Hard refresh the browser
- Press Ctrl+F5 (Windows/Linux) or Cmd+Shift+R (Mac)
- Or clear browser cache completely
- Check browser console
- Press F12 to open developer tools
- Look for errors in the Console tab
- Common errors:
404- Script not found (plugin not installed)CORS- WebSocket blocked (check CORS config)
- Verify plugin is installed
- Dashboard > Plugins should show “OpenWatchParty”
- Check Jellyfin logs for plugin load errors
- Validate automatic injection runtime (File Transformation)
- Registration alone is not sufficient. Confirm callback execution in logs.
- Look for OpenWatchParty logs:
Registered File Transformation pattern 'index.html' -> callback 'TransformIndexHtml'Registered File Transformation pattern 'home-html\..*\.chunk\.js' -> callback 'TransformHomeChunkScript'TransformIndexHtml invoked for file ...TransformHomeChunkScript invoked for file ...
- Validate script endpoint:
curl -I http://localhost:8096/OpenWatchParty/ClientScript - If endpoint is reachable but callback logs never appear, keep manual Custom HTML injection enabled until integration is fixed.
Cannot Connect to Session Server
Symptoms:
- “Offline” status in the panel
- Console shows WebSocket errors
Solutions:
- Check server is running
# Docker docker ps | grep session # Check health endpoint curl http://localhost:3000/health - Check the network path
# Test the local server health endpoint nc -zv localhost 3000Keep raw port 3000 private. For remote guests, publish HTTPS/WSS through the reverse proxy and route
/wsto the session server; see Deployment. - Check WebSocket URL
- Browser console will show the attempted URL
- Should be
ws://host:3000/wsorwss://host:3000/ws
- Check CORS
- Session server logs show CORS errors
- Set
ALLOWED_ORIGINSto include your Jellyfin URL
Invite button says Room not found (or 404)
The reverse proxy is not routing /invite to the session server. Configure both the WebSocket and invite routes as shown in Reverse Proxy Configuration.
Sync Issues
Symptoms:
- Participants out of sync
- Playback drifts apart over time
- Frequent jumping or stuttering
Solutions:
- Wait a few seconds
- Initial sync takes 2-3 seconds
- Drift correction works gradually
- Host: pause and play again
- This re-syncs all participants
- Check network quality
- High latency causes sync issues
- Check the latency value in the room bar
- Ideal RTT: < 100ms
- HLS/transcoding issues
- Transcoded streams have higher latency
- Try direct play if possible
- Reduce quality if network is slow
- Check if everyone has the same media
- Different versions may have different durations
- Ensure all users can access the media
Technical details: See Architecture - Clock Offset and Playback Drift and Sync Algorithms
Room queue is missing or does not advance
- Queue controls are available to the host in a room only when the server and client negotiate the
media_queuecapability. Confirm both sides are current and compatible; older clients can still follow the current room item but do not receive queue state. - A queue can contain up to 20 Jellyfin items. Episode autofill applies to the host’s episodic item; it does not create a queue for a movie.
- Only the host can change the queue. The countdown is 10 seconds and is canceled by a pause, the host seeking back before the credits, removing the next item, or changing the room item.
- Rooms and queues live in memory. A server restart closes the room and clears its queue; reconnect by creating a new room.
See Configuration: Room queue and lifecycle for the persistence and capability details.
Room Closes Unexpectedly
Symptoms:
- “Room closed” notification
- Participants disconnected
Causes:
- Host disconnected
- Host’s network dropped
- Host closed the browser
- Host’s computer went to sleep
- Server restart
- Rooms, participant lists, queues and countdowns are in-memory (ephemeral)
- Check if server was restarted
- Network issues
- Check host’s connection
- Check server logs for errors
Technical details: See Architecture - Host Network Disconnect
Authentication Errors
Symptoms:
- Cannot join rooms
- “Unauthorized” errors
- Token issues
Solutions:
- Check the selected JWT mode and keys
- In
hs256, the plugin JWT Secret must match serverJWT_SECRET; both are required in production. - In
hybrid, the shared secret is still required for legacy HS256 tokens, and the server also needs a valid public-key trust store for RS256. - In
asymmetric, the plugin’s active RSA key must be present in the server trust store;JWT_SECRETis not used. - Empty secrets are allowed only in explicitly enabled local development mode on both sides. Never expose that mode.
- In
- Check token expiration
- Default: 1 hour
- Refresh the page to get a new token
- Rate limiting
- Max 30 token requests per fixed one-minute window
- Wait and try again
Jellyfin 12: every OpenWatchParty request returns 401
Symptoms:
- The Watch Party panel loads but no request succeeds
401 Unauthorizedresponses in the browser console or Jellyfin logs
Cause:
Jellyfin 12 disables legacy authorization. The DisableLegacyAuthorization migration turns off EnableLegacyAuthorization, so the legacy X-Emby-Token header no longer resolves and only Authorization: MediaBrowser Token="..." is accepted.
OpenWatchParty 0.7.0 is the current Jellyfin 12 plugin release and sends the modern Authorization header. 0.3.4 is the current release for Jellyfin 10.11. Do not assume historical plugin releases target or support Jellyfin 12; check the compatibility matrix.
Solutions:
- Upgrade OpenWatchParty
- Install
0.7.0on Jellyfin 12, or0.3.4on Jellyfin 10.11, from the compatible plugin repository entry or manually. - Verify the installed version in Dashboard > Plugins > OpenWatchParty.
- Install
-
Temporary stopgap only
As a short-lived workaround, set the following in Jellyfin’s
system.xml:<EnableLegacyAuthorization>true</EnableLegacyAuthorization>Then restart Jellyfin.
This is deprecated and unsupported long term. It re-enables a legacy authentication path that Jellyfin 12 is removing and only restores access until you can upgrade. Do not rely on it.
The WebSocket connects but the session server rejects the token
Symptoms:
- The OpenWatchParty panel loads but never joins a room
- The session server logs
Client connected: …immediately followed byAuth failed for …: Invalid token: InvalidSignature - The plugin diagnostics may report
authentication_configurationas passing
Cause:
In hs256 and hybrid modes, the plugin signs with JwtSecret and the server verifies with JWT_SECRET; mismatched values cause InvalidSignature. In asymmetric mode, check that the plugin’s active RSA key is present in the server trust store and that the issuer and audience match.
The plugin’s Allow insecure unauthenticated development setting does not work around this. It only makes the plugin omit the token, it is ignored while a JwtSecret is configured, and the session server independently requires ALLOW_INSECURE_NO_AUTH=true before it accepts unauthenticated clients.
Solutions:
- Managed deployments (installed with
owpctl): runowpctl doctor, which performs an authenticated WebSocket ping/pong and reports this failure directly. For HS256 or hybrid, useowpctl configure --rotate-jwt-secret --yes --api-token-file <protected-file>to update the shared secret on both sides. In asymmetric mode, verify the trust store and useowpctl pairto register the plugin’s current signing key. - Manual HS256 or hybrid deployments: set the same strong value in Dashboard → Plugins → OpenWatchParty → JWT secret and in the session server’s
JWT_SECRETenvironment variable, then restart the session server. For asymmetric mode, ensure the active plugin public key is in the server trust store. Confirm the server started withJWT authentication is enabledand check forInvalidSignatureafter a browser reload.
Diagnostics report the session server as unreachable
Symptoms:
Run diagnosticsfails thesession_httpcheck withSESSION_UNREACHABLEorConnection refused (host:3000)- The browser still appears to connect, or the panel times out while
just uplooks healthy
Cause:
The health probe runs inside the Jellyfin process, so it resolves the session server from Jellyfin’s network namespace — not the browser’s. A same-host auto-detected URL such as ws://192.168.1.10:3000/ws can be reachable from the browser and refused from the Jellyfin container, especially when the two run on different Docker networks or the port is only published on loopback.
Solutions:
- Set an explicit Session server URL (
ws://host:port/ws) that is reachable from both Jellyfin and the browser, rather than relying on same-host auto-detection. - In Docker, put Jellyfin and the session server on a shared network and use the service name (for example
ws://owp-session-server:3000/ws) if the browser reaches it through a reverse proxy. - Re-run
Run diagnostics:session_httpmust report the session server version and protocol before a watch party can work.
HLS Streaming Issues
Symptoms:
- Sync works initially but drifts during playback
- Frequent buffering interrupts sync
- Position jumps backward unexpectedly
- “False pauses” trigger unwanted sync events
Understanding HLS:
HLS (HTTP Live Streaming) breaks video into small segments (typically 2-10 seconds). This causes:
- Buffering gaps between segments
- Position reporting delays
readyStatechanges during segment loads
Solutions:
- Buffering during sync
Problem: Video buffers, reports paused state, triggers sync issues Solution: OpenWatchParty filters these "false pauses" automatically If issues persist: reduce video quality to improve buffering - Position jumps backward
Problem: HLS reports position from previous segment during load Solution: Server ignores small backward jumps (< 2s) automatically If large jumps: check network stability, try different quality - Segment loading delays
- Reduce bitrate/quality in Jellyfin playback settings
- Use direct play instead of transcoding when possible
- Check network bandwidth between client and Jellyfin server
- Direct play vs transcoding
# Check if transcoding is happening # In Jellyfin Dashboard > Playback > Active Devices # Look for "Transcoding" indicator # For better sync, prefer: # - Direct Play (no transcoding) # - Direct Stream (container change only) # Avoid if possible: # - Transcoding (full re-encode) - Safari-specific HLS issues
- Safari uses native HLS (not hls.js like other browsers)
- May report
readyStatedifferently during buffering - Workaround: Keep Safari tab in focus, or use Chrome/Firefox
Technical details: See Sync Algorithms - HLS Handling
Rate Limiting Issues
Symptoms:
- “Rate limit exceeded” error
429while requesting a Jellyfin token or opening a WebSocket at the connection limit- WebSocket closes with policy code
1008after exceeding the per-connection message limit - Cannot get new token
Types of rate limiting:
| Limit | Value | Applies to |
|---|---|---|
| Token requests | 30 per fixed 60-second window | Per Jellyfin user; excess token requests return HTTP 429 |
| WebSocket messages | 30 per 1000 ms window | Per connection; excess closes the WebSocket with policy code 1008 |
| WebSocket connections | 256 total; 32 per effective IP by default | Excess upgrades return HTTP 429 |
| Message size | 64 KiB per frame and assembled message | WebSocket input |
Solutions:
- Token rate limit (30/min)
Cause: Too many page refreshes or reconnection attempts Solution: Wait 1 minute, then refresh once Prevention: Don't rapidly refresh the page - Message rate limit (30/sec)
Cause: A client sent over 30 messages within a 1000 ms window Solution: Wait for the client to reconnect; avoid rapid repeated actions Note: Normal usage won't hit this limit - Debugging rate limits
# Check server logs for rate limit messages docker logs session-server 2>&1 | grep -i "rate" # Token endpoint logs "Token rate limit exceeded"; the session server logs "Rate limited" - If rate limited frequently
- Check for browser extensions that might cause extra requests
- Check for scripts auto-refreshing the page
- Verify you’re not running multiple tabs with OpenWatchParty
Panel Opens But Empty
Symptoms:
- Panel slides out but shows nothing
- No room list
Solutions:
- Wait for connection
- WebSocket may still be connecting
- Check for “Connecting…” status
- Check console for JavaScript errors
- Press F12 and check Console tab
- Look for script loading errors
- Clear browser cache
- Old script version may be cached
Log Analysis
Session Server Logs
# Docker logs
docker logs session-server
# Follow logs
docker logs -f session-server
What to look for:
| Log Message | Meaning |
|---|---|
Client connected |
New WebSocket connection |
Client disconnected |
Connection closed |
Room created |
New room started |
Room closed |
Room ended |
SECURITY: Wildcard origin |
CORS warning |
Message too large |
Size limit exceeded |
Invalid token |
Authentication failure |
Jellyfin Logs
Location varies by installation:
- Docker:
docker logs jellyfin - Linux:
/var/log/jellyfin/ - Windows:
%ProgramData%\Jellyfin\Server\log\
Look for:
[OpenWatchParty] JWT authentication is enabled.
[OpenWatchParty] JwtSecret is not configured.
Browser Console
- Press F12 to open Developer Tools
- Go to Console tab
- Filter by “OWP” or “OpenWatchParty”
Common messages:
| Message | Meaning |
|---|---|
[OWP] Loaded |
Client initialized |
[OWP] Connected |
WebSocket connected |
[OWP] Disconnected |
WebSocket closed |
[OWP] Room joined |
Successfully joined room |
Network Debugging
Check WebSocket Connection
In browser console:
// Check if connected
console.log(OWP.state.ws?.readyState);
// 0 = CONNECTING, 1 = OPEN, 2 = CLOSING, 3 = CLOSED
Test Session Server
# Health check
curl http://localhost:3000/health
# WebSocket test (requires wscat)
npm install -g wscat
wscat -c ws://localhost:3000/ws
Check Network Traffic
- Open Developer Tools (F12)
- Go to Network tab
- Filter by “WS” for WebSocket
- Click on the WebSocket connection
- View Messages to see all communication
Performance Issues
High CPU Usage
Session Server:
- Check number of rooms/clients
- Compare active connections and rooms with the configured limits; the server does not share in-memory rooms between replicas.
Client:
- Reduce video quality
- Close other tabs
- Check for JavaScript errors
Memory Usage
Session Server:
- Rooms are in-memory
- There is no verified per-client or per-room RSS estimate. Use container metrics and the measured load runs for your deployment; the sample measurements are not a capacity guarantee.
- The room queue is capped at 20 media items. Its serialized-size regression tests do not establish a process memory or out-of-memory result.
- Restarting the server closes rooms and discards the in-memory queue state.
Slow Sync
- Reduce
SYNC_LEAD_MSif latency is low - Increase if latency is high
- Check client hardware (old browsers may struggle)
Getting Help
Information to Provide
When reporting issues, include:
- Environment
- Jellyfin version
- Browser and version
- Operating system
- Docker version (if applicable)
- Configuration
- Session server settings (without secrets!)
- Plugin configuration
- Reverse proxy setup
- Logs
- Session server logs
- Jellyfin logs
- Browser console output
- Steps to Reproduce
- What you did
- What you expected
- What happened instead
Where to Get Help
- GitHub Issues - Bug reports
- GitHub Issues - Questions (choose the question template)
- Jellyfin Forums - Community help
Reset Procedures
Reset Client State
- Clear browser cache and cookies
- Hard refresh (Ctrl+F5)
- Or in console:
localStorage.clear(); location.reload();
Reset Server State
# Restart session server (clears all rooms)
docker restart session-server
Reset Plugin Configuration
- Dashboard > Plugins > OpenWatchParty
- Clear all fields
- Save
- Restart Jellyfin
Complete Reset
- Stop all services
- Remove plugin DLL
- Clear Jellyfin config cache
- Reinstall from scratch