Skip to content

fix(service, routing): sanitize auth errors, fix response header order, and pass parent ctx - #4709

Open
GeekatplayStudio wants to merge 1 commit into
livekit:masterfrom
GeekatplayStudio:fix/service-routing-cleanups
Open

fix(service, routing): sanitize auth errors, fix response header order, and pass parent ctx#4709
GeekatplayStudio wants to merge 1 commit into
livekit:masterfrom
GeekatplayStudio:fix/service-routing-cleanups

Conversation

@GeekatplayStudio

Copy link
Copy Markdown
  1. Fix Content-Type header ordering in HandleErrorJson (pkg/service/utils.go)
    Right now w.Header().Add("Content-type", ...) is called after handleError() (which calls w.WriteHeader()) and after writing the response body. In Go's net/http, any header modifications after WriteHeader or writing the body get ignored by the runtime. Moved w.Header().Set("Content-Type", "application/json") to before headers are written to the connection.

  2. Sanitize HTTP 401 error payloads in APIKeyAuthMiddleware (pkg/service/auth.go)
    Currently when token parsing or verification fails in auth middleware, the raw authToken string and API key are concatenated directly into the error payload returned in the 401 body (errors.New("invalid token: "+authToken...)). This can reflect raw tokens back to clients or proxy logs unnecessarily. Swapped these to use the already defined package errors (ErrInvalidAPIKey and ErrInvalidAuthorizationToken).

  3. Pass caller ctx in RedisRouter.ClearRoomState (pkg/routing/redisrouter.go)
    ClearRoomState was accepting _ context.Context but hardcoding context.Background() for the Redis HDel call. Replaced context.Background() with the passed ctx so parent cancellation, timeouts, and tracing spans are preserved.

Let me know if anything looks off or if you'd prefer any of these split out into separate PRs. Thanks!

@GeekatplayStudio
GeekatplayStudio requested a review from a team as a code owner July 29, 2026 15:02
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Geekatplay Dev seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review

Comment on lines +116 to 118
func (r *RedisRouter) ClearRoomState(ctx context.Context, roomName livekit.RoomName) error {
if err := r.rc.HDel(ctx, NodeRoomKey, string(roomName)).Err(); err != nil {
return errors.Wrap(err, "could not clear room state")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Room-to-server routing records may stop being cleaned up when a room closes

The routing cleanup for a closed room now runs with the session's request context (ClearRoomState(ctx, ...) at pkg/routing/redisrouter.go:116-117) which is typically already cancelled by the time the room actually closes, so the leftover room-to-server mapping is never deleted.
Impact: Stale routing entries linger in Redis and can misdirect later attempts to create or join a room with the same name to a node that no longer hosts it.

Why the passed context is usually already cancelled at cleanup time

ClearRoomState is only ever invoked from RoomManager.deleteRoom (pkg/service/roommanager.go:204), which itself only runs inside the room's OnClose callback (pkg/service/roommanager.go:700). The ctx captured by that closure is the context of the participant/session that first created the room via getOrCreateRoom (pkg/service/roommanager.go:642, called from StartSession/CreateRoom with request-scoped contexts originating from HandleSession at pkg/service/signal.go:116). A room's OnClose fires when it becomes empty (all participants gone), by which point the creating session's context is almost always already done. Previously the code deliberately used context.Background() so the Redis HDel on NodeRoomKey always executed; with the passed ctx cancelled, HDel returns context canceled and the room_node_map entry is left behind. This mirrors the reasoning already documented for UnregisterNode at pkg/routing/redisrouter.go:81-82 ("could be called after Stop(), so we'd want to use an unrelated context").

Prompt for agents
ClearRoomState in pkg/routing/redisrouter.go was changed to use the passed ctx instead of context.Background() for the Redis HDel on NodeRoomKey. However, the only caller path is RoomManager.deleteRoom (pkg/service/roommanager.go:204), which runs exclusively inside the room's OnClose callback (pkg/service/roommanager.go:700). The ctx captured there is the request/session context of the participant that first created the room (via getOrCreateRoom / StartSession / CreateRoom). By the time the room closes (becomes empty), that context is typically already cancelled, so HDel fails with context canceled and the room->node routing entry is never removed, leaving stale state in Redis. Consider either keeping context.Background() for the Redis HDel here (as UnregisterNode does), or having the caller pass a non-cancellable context (e.g. context.WithoutCancel(ctx)) for the cleanup path so that cancellation of the originating session does not prevent routing cleanup. If tracing/timeout propagation is desired, context.WithoutCancel combined with a fresh timeout would preserve trace values without inheriting cancellation.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants