Dispatcher still sends to player even after presence has been deleted

I am currently tackling the leave feature in my game using TypeScript. The server correctly receives the message and deletes the corresponding presence using delete state.presences[p.sessionId];
However, the player still receives messages from the game even after having its presence record deleted. How does the broadcast function actually work?

Client side

    public void OnLeave()
        LeaveMessage message = new() { playerId = gameStateManager.PlayerID, };
        NakamaConnection.SendMessage(OpCodes.LEAVE_GAME, message);
        //QuitMatch(); ---> Weirdly this function call does not work

    /// <summary>
    /// Quits the current match.
    /// </summary>
    public void QuitMatch()
        // Ask Nakama to leave the match.

        // Reset the currentMatch and localUser variables.
        currentMatch = null;
        localUser = null;

Server side

// Correctly deleting the presence on message receive
case OpCode.LEAVE_GAME:
        logger.debug(`Leave Game ${message.sender.username}`);
        delete state.presences[message.sender.sessionId];
        if (state.game_state == GameState.LOBBY) {
          sendPlayersInLobby(state, dispatcher);
        } else {
          let player = getPlayerFromId(state, message.sender.username);
          for (let i = 0; i < state.players.length; i++) {
            if (player?.username == state.players[i].username) {
              state.players[i].hp -= 99;
              removeDead(state, dispatcher, logger);

// Problem can then be seen here among other places
      const playersPresence: nkruntime.Presence[] = [];
      for (let userId in state.presences) {
        let presence = state.presences[userId];
        if (presence) {
      playersPresence.forEach((presence) => {
        //Output nothing because no presence is found when playing against bots
      logger.debug("Time up");
      // Still sends to the player that has been removed

Am I doing something wrong?

Hello @StudioParaNoya,

Is anything shown in the logs when NakamaConnection.Socket.LeaveMatchAsync(currentMatch); is called?

Hello @sesposito,

I don’t have the logs anymore since I posted the question. I was able to fix it by doing the following:

function BroadCastMessage(
  state: State,
  dispatcher: nkruntime.MatchDispatcher,
  opCode: number,
  message: string = "",
  players?: nkruntime.Presence[] | null,
  isDuel: boolean = false,
  logger?: nkruntime.Logger
) {
  logger?.debug("Inside broadcast");

  const availablePresence: nkruntime.Presence[] = [];
  var presenceToBroadcast: nkruntime.Presence[] = [];

  // Initially gather all precences
  for (let userId in state.presences) {
    let presence = state.presences[userId];
    if (presence) {

   // If I want to broadcast to specific players, I go through the list of players and keep only the ones with a presence still active. If not, consider the whole list as the list to broadcast to
  if (players) {
    for (let player of players) {
      for (let presence of availablePresence) {
        if (player.userId == presence.userId) {
  } else {
    presenceToBroadcast = availablePresence;

  // If duel, handle the message later. If not, simply broadcast it to all presenceToBroadcast
  if (isDuel) {
  } else {
    dispatcher.broadcastMessage(opCode, message, presenceToBroadcast);

Most likely not the best solution, but it gets the job going for now. If you have any advice on how to fix it, it would be really appreciated!

This approach is what I was going to suggest, however I was wondering why LeaveMatchAsync did not work for you, I don’t see why it shouldn’t.

I am using
For nakama: image: registry.heroiclabs.com/heroiclabs/nakama:3.16.0
For the SDK: Version 3.10.1

Since I started using Nakama, I felt like I missed something during the initial setup. I will try to request a call with the support team, maybe they will be able to figure out what is wrong.

Thanks a lot @sesposito!