24

For my Node application, I have a server (app.js) running on Debian serving both html and websocket data using socket.io to my client (index.html). I am attempting to make a turn-based HTML5 multiplayer game.

After performing a number of successful data transmissions using socket.emit()/io.emit() and socket.on(), my server crashes on an socket.emit() call with the error
"events.js:72
throw er; //Unhandled 'error' event
RangeError: Maximum call stack size exceeded".
I have quite a few socket.on() event listeners, with each one handling a different function in the game (e.g. roll_dice, end_turn, ready_to_play, etc.).

I attempted researching the problem (found a lot of discussion on async loops), but was unable to find how to apply the solutions to my own code. I attached relevant source here. You can also view all the source on my github at: https://github.com/sjmoon0/gameofdeath

index.html

var socket = io.connect('http://131.178.15.173',{'forceNew':true});
 
                    ...

  //----------------Initialization and Menu functions-----------
  socket.on('load', function (data) {
    console.log(data);
    clientID=data;
    socket.emit('check_game_started', { un: clientID });
    socket.on('last_client_loaded', function(hasStarted){
     console.log("Has game started? :"+hasStarted);
     if(hasStarted==true){
      $('#choosecharacter').show();
     }
    });
  });

  socket.on('client_disconnect', function (data) {
    console.log(data);
  });

  socket.on('client_counter', function (data) {
    if(data<5){
     console.log(data);
     incrementLoadBar(data); 
     allowedInGame=true;
    }
    if(!allowedInGame){
     ...
    }
  });

  socket.on('game_started', function (data) {
    console.log(data);
    $('#welcome').hide();
    $('#choosecharacter').show();
  });

  socket.on('set_user', function(characterName){
   chosenCharacter=characterName;
  });

  socket.on('disable_player_choice', function(data){
   var id=data.stuff[0].chara;
   incrementLoadBar(data.stuff[0].numChar);
   console.log(id +" was chosen");
   $('#'+id).hide();
  });


//-------------------Gameplay functions
  socket.on('start_gameplay',function(nonsense){
   showChanges(nonsense);
 $('#wait').hide();
 $('#gamespace').show();
 draw_c();
 socket.emit('ready_to_play',chosenCharacter);
  });

  socket.on('take_turn',function(updatedBoard){
   showChanges(updatedBoard);
   if(updatedBoard.currPlayer==chosenCharacter){
    promptUser(updatedBoard);
   }
  });

  socket.on('roll_result',function(rollResult){
   promptUser(rollResult);
  });

                  ...
                  

 $('#rollDiceButton').click(function(){
  socket.emit('roll_dice',chosenCharacter);
 });

 $('#okCloseButton').click(function(){
  socket.emit('end_turn',chosenCharacter);
 });

 $('.thumbnail').click(function(something){
  socket.emit('player_chosen', something.target.id);
              ...
 });

app.js

var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
var url = require('url');

...

app.listen(8001);

function handler (req, res) {
...
}
console.log("~Server Running~");


io.on('connection', function (socket) {
  console.log("A Client connected");
  ...
  socket.emit('load', { user: uID });
  io.emit('client_counter',numClients);

  if(numClients==4){
      gameStarted=true;
      console.log("Game started!");
      io.emit('game_started',"The Game has begun!");
    }
    else if(numClients>4){
      numClients--;
      delete allClients[allClients.indexOf(socket)];
    }

  socket.on('check_game_started', function (data) {
    socket.emit('last_client_loaded', gameStarted);
    console.log(data);
    if(gameStarted){
      console.log("Last Player Loaded!");
    }
  });

  socket.on('player_chosen', function(cp){
    ...
    socket.emit('set_user', cp);
    ...
    io.emit('disable_player_choice',{'stuff':[{'chara':cp,'numChar':numCharChosen}]});
    if(numCharChosen==4){
      io.emit('start_gameplay', boardUpdate);
    }
  });

  socket.on('disconnect',function(){
    console.log("A client disconnected");
    numClients--;
    delete allClients[allClients.indexOf(socket)];
    io.emit('client_disconnect',"We've lost another comrade!");
  });

  socket.on('ready_to_play',function(characterThatIsReadyToPlay){
    io.emit('take_turn',boardUpdate);
  });

  socket.on('roll_dice', function(characterThatRolledDice){
    var temp=generateRollResult(characterThatRolledDice)
    socket.emit('roll_result',temp);
  });

  socket.on('end_turn',function(characterThatEndedTurn){
    io.emit('take_turn',nextUpdate(characterThatEndedTurn));
  });
});

Please be gentle, I just started using Node.js about a week ago. Thanks!

noden00b
  • 1,081
  • 1
  • 7
  • 8

9 Answers9

74

Found my issue.

The object (temp) I was trying to send over the network (in socket.emit('roll_result',temp);) was a self-referencing array. It was the recursive property of the array that caused the stack to exceed the max size.

noden00b
  • 1,081
  • 1
  • 7
  • 8
  • Thanks for adding the cause of the issue, just ran into the same issue trying to send a self recursive object over a socket.io connection and wasn't aware what was causing the maximum call stack size to be exceeded! – Andrew Winterbotham Apr 20 '16 at 12:00
  • wow, just ran into this problem today as well! If I had not discovered this post, another year might have passed. lol – buycanna.io May 03 '19 at 20:46
  • Thanks so much, you solved my problem! – uyuyuy99 Jan 29 '22 at 22:34
10

The answer is very helpful. Thanks.

I just had the same issue and want to share with anyone who might encounter it.

My code uses express.js and has the following:

io = require('socket.io').listen(server);
......
io.to(socketId).emit('hello', {userId:userId, data:data, res:res});

It generated the "Maximum call stack exceeded" error. The problem is that I should not send the variable 'res' over the socketio to the client. I guess it will cause some recursive behavior if I do so.

The solution is just remove the 'res' from the emit statement:

io.to(socketId).emit('hello', {userId:userId, data:data});
Fan Gong
  • 191
  • 1
  • 3
5

I had the same issue. For me the problem was that the object I was emitting on the socket held a reference to the socket itself. So I was basically sending a socket inside a socket. Don't try this at home :)

0

This is the same error I came across, When I clicked on submit button. It was getting disabled and throwing "Maximum call stack size exceeded". I restarted my browser, It worked.. Strange behaviour

Mangesh Sathe
  • 1,987
  • 4
  • 21
  • 40
0

I would like to share my mistake on the same issue:

    public createClient() {
    this.client = new net.Socket();
    this.client.connect(this.port, this.ip, () => {
        this.emit("connect");
    });

    this.client.on("data", (data) => {
        this.emit("data", data);
    });

    this.client.on("close",function () => {
        this.emit("close");
    });

}

in close event, using normal function and this ( cause of error ) cause a loop of close event emit and the result is stack size error .

    this.client.on("close", () => {
        this.emit("close");
    });

Using arrow func. ( or copying real this into something like _this and using inside function ) solved issue.

This is actually being ignorant about function context ( in callback ), or negligence ..

firatto
  • 19
  • 1
  • 9
0

I had the same problem, it happened because I was sending 3 objects with the emit I solved it by sending only one object

Amera Abdallah
  • 638
  • 7
  • 5
0

I had the same issue too, but in my case it's a bit funny because I actually was emitting the error event socket.on("error", new Error(err)) in my catch block. It was fixed with just changing the emitted event because error is a private event. LOL

McWally
  • 217
  • 2
  • 5
0

In my case, I was accidentally sending a Promise through socket.io

TheBotlyNoob
  • 369
  • 5
  • 16
-1

I ran into the same problem while i was making a real-time whiteboard application using Socket.io, express server and p5.js library. i was trying to send the p5 vector using socket to the server whick caused the error.

Here is my code txtPos = createVector(mouseX / width, mouseY / height); let data = { pos: txtPos, txt: "", h: colorSlider.value(), b: brighSlider.value(), size: sizeSlider.value(), }; texts.push(data); socket.emit("newText", data);

^ The above code throws an error as the data object i wanted to send was including the txtPos(p5 vector) which includes the p5 object, which includes the p5 vector, which again includes the p5 object and it goes on forever...like a recursive object(circular object).

to fix this my new code was pretty simple.

I replaced

pos:txtPos,

with

pos:{ x:txtPos.x, y:txtPos.y, } and guess what now i am able to send this data successfully. :D

  • Although the error you have faced is similar, the problem and solution context is different from OP's question. Also, it does not add any new value to existing answers. Please consider following the answer guideline: https://vi.stackexchange.com/help/how-to-answer. Best. – Ashish Singh May 07 '21 at 06:27