Troubleshooting
The real, common failure modes — what causes them, how to confirm, and how to fix.
TURN isn't being used / only host candidates appear
If chrome://webrtc-internals shows only host (internal) candidates, the relay never enters negotiation. Most often this is a server advertising an internal IP.
- Confirm your
iceServersactually reached theRTCPeerConnection(log the config you pass in). - On LiveKit, check
rtc.use_external_ipand the node's advertised address — an SFU behind NAT advertising a private IP suppresses relay candidates. - Force the relay path while testing with
iceTransportPolicy: "relay"; if nothing connects, the credentials or URLs are wrong, not the network.
No relay candidates over TCP or TLS
UDP works on your network but you see no candidates from the turn:…?transport=tcp or turns:…:443 URLs.
- Verify the client can reach
relay.usebaton.io:443outbound (many strict networks allow only outbound 443). - Ensure all four URLs are present in the array you pass — a trimmed array silently drops the TLS path.
- Re-test from the restricted network itself; office and home networks behave very differently.
Works on STUN but fails behind a strict corporate / hospital firewall
Direct/STUN connections succeed everywhere except inside locked-down enterprise or hospital networks. Those networks block UDP and inspect traffic (DPI), so plain TURN is filtered.
The fix is the turns:relay.usebaton.io:443?transport=tcp endpoint: TURN over TLS on 443 looks like ordinary HTTPS and traverses deny-all / DPI firewalls. Make sure it's in your iceServers and reachable — this is the endpoint that matters most for enterprise clients.
How do I confirm the relay is actually used?
- Open
chrome://webrtc-internalsduring a call and read the selected candidate pair — look fortyp relaywith therelay.usebaton.ioaddress. - Use the WebRTC Trickle ICE test tool: paste your
iceServersand check that arelaycandidate is gathered. - Set
iceTransportPolicy: "relay"in a test build so only relayed candidates are allowed — if the call connects, the relay path is healthy.
Credentials expired / 401
A credential stops working at the Unix expiry encoded in its username.
- Mint a fresh pair from the credentials API before expiry; for long calls, refresh ahead of the TTL.
- If a brand-new credential is rejected as expired, suspect clock skew — put your servers on NTP.
- Never cache one credential pair across users or for longer than its TTL.
Allowlisting our IPs with a client's firewall team
Enterprise clients often need to approve fixed relay IPs once. Baton's relay hostnames map to stable, allowlistable IPs, so you can share them with a firewall team and they won't change underneath you.
- Share the relay hostname (
relay.usebaton.io) and its static IPs, plus the ports:3478(UDP/TCP) and443(TLS). - For strict outbound-443-only networks,
turnson 443 is usually all that needs approving.
The exact static IP list for each region is provided with your project during onboarding. Ask the team and we'll send the current mapping to hand to your client's firewall admins.