Socket.io: RangeError: Maximum call stack size exceeded

Created on 4 Jul 2014  ·  32Comments  ·  Source: socketio/socket.io

I am just calling `ìo.sockets.emit('hey', data);and it will crash withRangeError: Maximum call stack size exceeded. I use it in other places in my app and it works fine. I am not repeating this (checked with console). The logs say the error is insocket.io/node_modules/has-binary-data/index.js:46``.

I dont know where the problem is. I tried logging io.sockets right before using it and it outputs this:

{ name: '/',
17:39:37 web.1  |   server:
17:39:37 web.1  |    { nsps: { '/': [Circular] },
17:39:37 web.1  |      _path: '/socket.io',
17:39:37 web.1  |      _serveClient: true,
17:39:37 web.1  |      _adapter: [Function: Adapter],
17:39:37 web.1  |      _origins: '*:*',
17:39:37 web.1  |      sockets: [Circular],
17:39:37 web.1  |      eio:
17:39:37 web.1  |       { clients: [Object],
17:39:37 web.1  |         clientsCount: 2,
17:39:37 web.1  |         pingTimeout: 60000,
17:39:37 web.1  |         pingInterval: 25000,
17:39:37 web.1  |         upgradeTimeout: 10000,
17:39:37 web.1  |         maxHttpBufferSize: 100000000,
17:39:37 web.1  |         transports: [Object],
17:39:37 web.1  |         allowUpgrades: true,
17:39:37 web.1  |         allowRequest: [Function],
17:39:37 web.1  |         cookie: 'io',
17:39:37 web.1  |         ws: [Object],
17:39:37 web.1  |         _events: [Object] },
17:39:37 web.1  |      engine:
17:39:37 web.1  |       { clients: [Object],
17:39:37 web.1  |         clientsCount: 2,
17:39:37 web.1  |         pingTimeout: 60000,
17:39:37 web.1  |         pingInterval: 25000,
17:39:37 web.1  |         upgradeTimeout: 10000,
17:39:37 web.1  |         maxHttpBufferSize: 100000000,
17:39:37 web.1  |         transports: [Object],
17:39:37 web.1  |         allowUpgrades: true,
17:39:37 web.1  |         allowRequest: [Function],
17:39:37 web.1  |         cookie: 'io',
17:39:37 web.1  |         ws: [Object],
17:39:37 web.1  |         _events: [Object] } },
17:39:37 web.1  |   sockets:
17:39:37 web.1  |    [ { nsp: [Circular],
17:39:37 web.1  |        server: [Object],
17:39:37 web.1  |        adapter: [Object],
17:39:37 web.1  |        id: 'RfgXeMgHeP_9SQC5AAAC',
17:39:37 web.1  |        client: [Object],
17:39:37 web.1  |        conn: [Object],
17:39:37 web.1  |        rooms: [Object],
17:39:37 web.1  |        acks: {},
17:39:37 web.1  |        connected: true,
17:39:37 web.1  |        disconnected: false,
17:39:37 web.1  |        handshake: [Object],
17:39:37 web.1  |        _events: [Object] },
17:39:37 web.1  |      { nsp: [Circular],
17:39:37 web.1  |        server: [Object],
17:39:37 web.1  |        adapter: [Object],
17:39:37 web.1  |        id: '7TEjGJjWzxObulClAAAD',
17:39:37 web.1  |        client: [Object],
17:39:37 web.1  |        conn: [Object],
17:39:37 web.1  |        rooms: [Object],
17:39:37 web.1  |        acks: {},
17:39:37 web.1  |        connected: true,
17:39:37 web.1  |        disconnected: false,
17:39:37 web.1  |        handshake: [Object],
17:39:37 web.1  |        _events: [Object] } ],
17:39:37 web.1  |   connected:
17:39:37 web.1  |    { RfgXeMgHeP_9SQC5AAAC:
17:39:37 web.1  |       { nsp: [Circular],
17:39:37 web.1  |         server: [Object],
17:39:37 web.1  |         adapter: [Object],
17:39:37 web.1  |         id: 'RfgXeMgHeP_9SQC5AAAC',
17:39:37 web.1  |         client: [Object],
17:39:37 web.1  |         conn: [Object],
17:39:37 web.1  |         rooms: [Object],
17:39:37 web.1  |         acks: {},
17:39:37 web.1  |         connected: true,
17:39:37 web.1  |         disconnected: false,
17:39:37 web.1  |         handshake: [Object],
17:39:37 web.1  |         _events: [Object] },
17:39:37 web.1  |      '7TEjGJjWzxObulClAAAD':
17:39:37 web.1  |       { nsp: [Circular],
17:39:37 web.1  |         server: [Object],
17:39:37 web.1  |         adapter: [Object],
17:39:37 web.1  |         id: '7TEjGJjWzxObulClAAAD',
17:39:37 web.1  |         client: [Object],
17:39:37 web.1  |         conn: [Object],
17:39:37 web.1  |         rooms: [Object],
17:39:37 web.1  |         acks: {},
17:39:37 web.1  |         connected: true,
17:39:37 web.1  |         disconnected: false,
17:39:37 web.1  |         handshake: [Object],
17:39:37 web.1  |         _events: [Object] } },
17:39:37 web.1  |   fns: [],
17:39:37 web.1  |   ids: 0,
17:39:37 web.1  |   acks: {},
17:39:37 web.1  |   adapter:
17:39:37 web.1  |    { nsp: [Circular],
17:39:37 web.1  |      rooms:
17:39:37 web.1  |       { '5MGPNOdO4th_dOuZAAAA': [],
17:39:37 web.1  |         '64rUhxxp--4Qk1MqAAAB': [],
17:39:37 web.1  |         RfgXeMgHeP_9SQC5AAAC: [Object],
17:39:37 web.1  |         '7TEjGJjWzxObulClAAAD': [Object] },
17:39:37 web.1  |      sids:
17:39:37 web.1  |       { RfgXeMgHeP_9SQC5AAAC: [Object],
17:39:37 web.1  |         '7TEjGJjWzxObulClAAAD': [Object] },
17:39:37 web.1  |      encoder: {} },
17:39:37 web.1  |   _events: { connection: [Function] } }

My code is:

`````` JAVASCRIPT

if (game.scoreTeamTwo > game.scoreTeamOne && game.scoreTeamTwo > game.scoreTeamThree && game.scoreTeamTwo > game.scoreTeamFour) {
game.winner = 2;
io.sockets.emit('CTFEnd', game);
}

//It´s just looking if team 1 won the game and when it does is emits CTFEnd and all the other game data
``````

Most helpful comment

This probably happened because you were trying to send an object with circular references resulting in recursive calls that exceeded the stack size.

All 32 comments

Found a "solution" to my problem. I can send every single piece of data sperated, but it DOES NOT work with objects

I have recently been getting that error also, the only difference being that it only occasionally happens for me. Been testing all morning and I have not been able to replicate the problem.

For me it was that it only wasn´t able to emit to everbody, when the obj was to big.

@BenBals if the object is too big, then a decent workaround is to send it in form of a string i.e. run JSON.stringify() on it

I got another work around, but I considered that.

:cry: looking into this

has the same problem when emitting a object of type:
{success: file, content: file.content} where file.content is Buffer object, and all other properties of file is strings.

had to add the field content directly to the objects because hasBin() only checks the first level of a object. But when it then tried to send the buffer it got 'Maximum call stack exceeded'

You can replicate this by emitting the socket object (one way at least) so (Below would be server to -> client )

socket.emit('crash', {socket:socket});

I'm curious if this has an actual fix though.

Any update on that issue ?
EDIT: My issue was that I was trying to send recursive objects.

Interestingly enough, I'm getting this same error when attempting to pass my Firebase data to the client side.

Error

node_modules/socket.io/node_modules/has-binary-data/index.js:46

for (var key in obj) {
                ^
RangeError: Maximum call stack size exceeded

Server-side

var DB = new Firebase('https://1234abcd.firebaseIO.com');
var USERS = DB.child("users");

io.sockets.on('connection', function(socket){

    socket.emit('test', {
        db: USERS
    });

});

해결책을 찾습니다...

Sorry, but my Chinese isn't that great. Would you be so kind to translate this into English.

Did a Google Translate & it comes out to "Find the Solution..." ... hardly worth responding to. For record, the solution is to not pass such large amount of data via socket.io ... it's a socket which was designed for quick, short responses. Break up your response or send it via ajax. Unsubscribing to this thread.

One thing you the socket.io code could do is just asynchronously iterate the object and its properties, something like:

var props = [];
var obj = { a: 1, b:2, c: { d: 3, e: 4 } };

function next_prop(callback){
  if (!props.length){
    setTimeout(callback);
    return;
  }
  var prop = props.shift();
  //do whatever with the prop, call parse_obj on it if it's an object
  setTimeout(next_prop);
}

function parse_obj(obj, callback){
  for (var i in obj){
    props.push(i);
  }
  setTimeout(function(){next_prop(callback);});
}

parse_obj(obj);

Of course, this isn't actual code you should use, because there needs to be wrapping of functions for each individual object in case they're nested, otherwise you'll have conflicts with what object is being parsed.

I just get that error too, my code is like: "io.sockets.emit('key', data);"
i try to simplify the "data" object and keep it small, and it worked.
--AND,i look into the src code,found this:

image_20150306180547

when your "data" object has recursive attribute refering, this code would crash.
e.g. proto > proto > proto ...

+1 would the core team like a async version of of binary checking i can help get that setup

I can reproduce the problem by sending the socket object :)

+1 IE11 shows 'out of stack space' in _hasBinary.

This error showed for me when I was trying to send the whole socket object back to the client. All I _really_ needed was socket.id so the error disappeared when I sent back a smaller item — in my case, socket.id instead of the whole socket object

This probably happened because you were trying to send an object with circular references resulting in recursive calls that exceeded the stack size.

@LordMajestros That fixed it for me! Thanks

@GerbenHofman you're welcome

I faced same error on NodeJS v4.4.4 and socket.io v1.4.5.

In my case, it happens after disconnect event.
And it happens only when I called socket.emit more than 200,000 times.

If the number of call socket.emit is around 100,000, this error never happen.

The parameter I use with emit is always string

Does this information helps you?

Just to add to @shunjikonishi , I've seen this happen as soon as I have sent exactly 100,000 events via io.emit. I have 1 socked connected, so seems like 100,000 is a hard cap.

Seems like this is a limit imposed by Node (or the JavaScript runtime) and Socket.io is holding a reference to something every time you call emit, so when you hit the 100,000th emit it just falls over.

Tested with Node 5.11.0 and Socketio 1.4.8. My version of Node is different (as well as my operating system etc, really no idea where this 100,000 comes from) than @shunjikonishi which may explain why I hit the limit at 100,000 whereas he got up to 200,000.

I ran the exact same code using ws.js and it worked fine, surpassed 100,000 socket emits without problem, so maybe it's a problem with Socket.io.

Is there any chance of Socket IO implementing a guard against recursive data being emitted from and failing with this error? A simple detection could prevent this error from happening and emit instead a more meaningful error, like "You may be trying to send circular data". That would make it easier to debug.

There are quite a few decent solutions for detecting circular data: https://stackoverflow.com/questions/14962018/detecting-and-fixing-circular-references-in-javascript

One short solution relies on JSON.stringify to find the problem:

function isObjectCircular(obj) {
    try {
        JSON.stringify(circularReference);
    } catch(err) {
        return (err.toString() === 'TypeError: Converting circular structure to JSON');
    }
    return false;
}

More details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value

@adamreisnz You are welcome to make a PR ;)

It always catches me off guard when someone mentions me 5 months after I made a comment 😆

I met this error too. And that's why I waste two hours to check my whole codes :<.
I think it caused by recursion object like A={B:{C:A}}, and obviously socket is a recursion object.
In fact, I guess it happened because in buffer.js, they use a recursion to find the length of data which you want to send. And then get the "maximum call stack size exceeded". If they use JSON.stringify() to get the length, there will be another clearly error about "circular structure". By the way, it has a bad key word to google!

I just ran into this as well.

hasBin will fill the stack with a recursive loop if trying to send an object that has a circular reference.

Shucks, I see there is a PR that has been open for 2 years that would fix this:

@dustingraham the fix does look great, though I'm not sure about the performance implications.

Also, even if it doesn't throw in the hasBinary method anymore, I'm afraid the JSON.stringify method that is called later in the code will still throw an error:

> var a = {};
undefined
> var b = { a: a };
undefined
> a.b = b
{ a: { b: [Circular] } }
> JSON.stringify(a)
Thrown:
TypeError: Converting circular structure to JSON
    at JSON.stringify (<anonymous>)
Was this page helpful?
0 / 5 - 0 ratings