Issue with channel leave hook not triggering on app force close or disconnect

Hi everyone,

I’m building a game using Nakama for the backend, and I’ve implemented a voice room feature similar to Clubhouse, where users can join a room and talk.

To track who is currently talking or holding the microphone, I’m using Nakama chat channels. I edit the channel message regularly to update the list of speakers or users requesting the mic.

When a user leaves the room, I remove them from the mic list (By editing message).

To handle this, I’m currently using the ChannelLeave hook, and it works fine most of the time.

However, I’ve noticed that in many cases—especially when a user force-closes the app or their internet disconnects suddenly—the ChannelLeave hook doesn’t seem to fire. As a result, the user is not removed from the speaker list, which leads to inconsistencies.

It seems that ChannelLeave is not always triggered reliably in those abrupt disconnection scenarios.

What’s the best way to handle this properly and efficiently?
I’d really appreciate any advice or best practices for ensuring users are removed from the mic list even when they leave unexpectedly.

Thanks a lot in advance!

Hello @Ahmadrezadl,

Can you clarify what ChannelLeave hook you’re using?

Hello @sesposito , thank you for you respone
This is my code

init:

initializer.registerRtAfter("ChannelLeave", afterChannelLeave);
function afterChannelLeave(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, output: nkruntime.EnvelopeChannelLeave | null, input: nkruntime.EnvelopeChannelLeave) {
    logger.info("user " + ctx.userId + " left chat " + input.channelLeave.channelId);
    let channelId = input.channelLeave.channelId;
    if (channelId.indexOf("Mic_") != -1) {
        userLeaveFromMic(nk, ctx.userId, undefined, channelId);
    }
}

My problem not solved yet :frowning:

@Ahmadrezadl that hook will take care of any client that gracefully leaves the channel. To pick up on forceful disconnects, you’d need to add a hook on built in events: Events - Heroic Labs Documentation .

Hope this helps.

Where Should i call my event? I didn’t understand
How can I notified about player forcefully disconnected?

The SessionEnd event is created when the socket connection for a session is closed. The socket could have closed for a number of reasons but can be observed to react on.

You need to register your custom code as part of the hook:

func eventSessionEnd(ctx context.Context, logger runtime.Logger, evt *api.Event) {
   logger.Debug("process event session end: %+v", evt)
}

// initializer runtime.Initializer
if err := initializer.RegisterEventSessionEnd(eventSessionEnd); err != nil {
   return err
}

The event is generated automatically when a disconnect is detected by the server. This may overlap with the graceful disconnect, you may only need the former.

Thank you again for your response @sesposito
but there is no RegisterEventSessionEnd function on initializer on typescript server runtime :frowning:
I’m on the latest version btw

Edit: I read the document again and i see this:

Code snippet for this language TypeScript has not been found. Please choose another language to show equivalent examples.

So we don’t have this on typescript

Any plan to adding it?

Hi @Ahmadrezadl, sorry I forgot to mention that this API is Go only. JS support for the event pipeline is not part of the roadmap for now, but you can use Go just for this piece of code, which should be fairly straightforward, and keep using JS for the rest.