401 "token revoked" error on room rejoin after call disconnect (Android,iOS SDK)

Hi team,

We’re running into a reproducible issue where a participant gets a 401 / “token revoked” error when trying to rejoin a LiveKit room — but only after they’ve been on an active call for more than ~1 minute. This happens on both Android and iOS, which suggests it’s a server-side behavior rather than a client SDK issue.

Setup

  • We have a live streaming room where User A is the host
  • User B joins the room and can connect to User A via a 1:1 call inside the room
  • We reuse the same access token for User B across join/rejoin cycles within the same room (new tokens are only generated when a new room is created)
  • Android SDK version: (please fill in)
  • iOS SDK version: (please fill in)
  • LiveKit Server: (cloud or self-hosted + version)

Steps to Reproduce

  1. User A starts a live room
  2. User B joins the room using a token
  3. User B connects to a call with User A and stays on the call for more than ~1 minute
  4. User B ends the call and exits the room (disconnect)
  5. User B tries to rejoin the same room using the same token
  6. User B gets the following errors:
io.livekit.android.room.RoomException$ConnectException: Could not fetch region settings: 401

and

token revoked

(Equivalent error on iOS as well.)

What Works Fine (No Error)

  • If User B joins the room without connecting to a call and then exits → rejoin with same token works :white_check_mark:
  • If User B joins the call but disconnects within ~30 seconds → rejoin with same token works :white_check_mark:
  • The issue only occurs when User B has been on an active call (publishing/subscribing to tracks) for a longer duration (~1 min+)
  • Same behavior on both Android and iOS

Questions

  1. Does LiveKit server intentionally revoke a participant’s access token after they disconnect from a session where they were actively publishing/subscribing to media tracks for a sustained period?
  2. Is there a server-side configuration or token claim (like a session duration or single-use flag) that could be triggering this revocation?
  3. Is the expected pattern to generate a fresh token for every room.connect() call, even for the same room? The community post on token reuse suggests it should work as long as the token hasn’t expired — so what would cause an explicit revocation?

Happy to share client logs from both platforms or any additional details.

Thanks!

I believe this section of the docs is relevant: Tokens & grants | LiveKit Documentation

My guess is that < 1 minute is being interpreted as reconnection owing to (for example) network issues.

More than a minute (or whatever the actual ceiling is) is hitting the token revocation logic described at that docs link.

In either case, the suggestion would be to request a new token.

Generate a fresh token on every connect() call, even for the same room.

LiveKit revokes tokens after extended sessions (~1 min+) by design. Reusing them across reconnects is what’s
triggering the 401.

Seen this on production mobile deployments, token-per-connect fixed it completely.

You can configure the expiration of the token when it is minted.

@CWilson Thanks for the clarification. Generating a fresh token on every connect() makes sense. Just to add, setting an explicit TTL when minting keeps things predictable across reconnects as well.

@Sanchit_Gera, the mechanism behind “token revoked” is server-side revocation tied to the RemoveParticipant API. Open PR livekit/livekit#4344 (March 5, 2026) formalizes it: “adds a basic token block list that serves to block the access token after calling the RemoveParticipant function” [ livekit/livekit#4344 ]. Multi-node sync via Redis, with the token added to the block list when RemoveParticipant fires.

That fits your symptom: <30s call disconnects don’t hit a RemoveParticipant path, token stays valid for rejoin; >1min calls are ending in a way that triggers RemoveParticipant somewhere (your app code, a backend event handler, or an auto-cleanup hook), which revokes the token. Check whether anything in your server-side code or your room-event handlers calls RemoveParticipant on User B when User B’s call ends.

If token reuse is what you want, don’t call RemoveParticipant on the participant you want to rejoin. If RemoveParticipant is intentional, mint a fresh token per connect with explicit TTL, as CWilson noted.