RangeError: Maximum call stack size exceeded



  • I'm receiving a "RangeError: Maximum call stack size exceeded" from the game code, but the stack trace isn't providing from where in my code it is coming from.  It appears to be occurring when I am navigating between rooms; hwoever, it's not 100% reproducable.

    Has anyone run into this before and know how to avoid it?


    RangeError: Maximum call stack size exceeded
    at RegExp.test (native)
    at Object.findRoute (/opt/engine/dist/game/map.js:87:38)
    at Object.findExit (/opt/engine/dist/game/map.js:236:30)
    at Room.findExitTo (/opt/engine/dist/game/rooms.js:1032:29)
    at Room.wrappedFunction [as findExitTo] (profiler:96:29)
    at RoomPosition.findPathTo (/opt/engine/dist/game/rooms.js:1137:32)
    at RoomPosition.wrappedFunction [as findPathTo] (profiler:96:29)
    at endNodes.objects.forEach.i (/opt/engine/dist/game/rooms.js:1224:93)
    at Array.forEach (native)
    at RoomPosition.findClosestByPath (/opt/engine/dist/game/rooms.js:1221:30)



  • I ended up tracing the error to the code below.  I'm not sure what I'm doing wrong though :/.  The findClosestByPath call also uses far more than 2CPU (which is default) per call when this error occurs (it's closer to ~25 CPU/call).

     

    var droppedEnergy =
    creep.room.droppedEnergy || creep.room.find(FIND_DROPPED_ENERGY, {filter: (source) => source.energy > 0});
    creep.room.droppedEnergy = droppedEnergy; // Per-tick caching
    if (!!droppedEnergy.length && !!!sources.length && !!mustFillCarry) {
    sources = _.filter(droppedEnergy, (source) => source.energy > (creep.carryCapacity - creep.carry.energy));
    }
    if (!!droppedEnergy.length && !!!sources.length && !!!mustFillCarry) {
    sources = droppedEnergy;
    }

    var targetSource = !!sources.length && creep.pos.findClosestByPath(sources); // Error starts here

  • Culture

     

    Why are there so many exclamation points.



  • Your code doesn't any recursive call, so I don't think the problem lies there (unless you modified the prototypes of the game objects).

    But since findClosestByPath seems to generate a large call stack by itself, this is why the problem becomes apparent there. Try swapping it by a dummy method (e.g. sources[0]), and you should get a more informative stack trace.



  • There seems to be a race condition with the object cache. Or a problem with the object cache server sync. Or something like that. 

    When setting a value to an object without saving that value to memory and without restoring it, the value should be gone. Instead it will be there, or not, you can't say - and that even for quite a while. 

    I created a test with an object I am creating outside the main loop. 

    A value can be set to the object using a function. The value will be logged to console each loop. After setting it, it should be logged only once and then gone forever (because I am not using the memory). But this is what happens: 

    [20:35:02]

    undefined
     
    FlagDir.setTest(1); // I am setting the value here

    [20:35:09]
    undefined

     
    undefined

    [20:35:13]
    1

    [20:35:17]
    undefined

    [20:35:20]
    1

    [20:35:24]
    undefined

      
    [20:35:26]
    undefined

    [20:35:29]
    undefined

    [20:35:32]
    undefined

    [20:35:35]
    undefined

    [20:35:38]
    1

    [20:35:41]
    undefined

    [20:35:44]
    undefined

    [20:35:47]
    1

    [20:35:50]
    undefined

    [20:35:53]
    undefined

     
    [20:35:56]
    undefined

    [20:35:58]
    undefined

    [20:36:01]
    undefined

    [20:36:04]
    1

    [20:36:07]
    1

    [20:36:14]
    undefined

    [20:36:18]
    undefined

    [20:36:21]
    undefined

    Maybe creep.room.droppedEnergy still contains the energy locations from the old room - you mentioned this occurs occationally at the room border...  

    As a walkaround, you can remove the value in the beginning of the loop.

    delete creep.room.droppedEnergy;

     Hope it helps...

    P.S.: This is the setTest function used...

    setTest: function(val){
      this._test = val;
    }

    And in the main loop:

    console.log(FlagDir._test);

     



  • Hernander: I had a bug due to a falsy object evaluating true once that was fixed with !!, I thus sprinkled it everywhere to be safe :/.  Plus, it makes it more exciting ;).

     

    saturn7: I"ve updated my code to try/catch the first findClosestByPath then retry with sources[0].  Once the error occurs again I'll post it here.

     

    cyberblast: That's an interesting find.  I did find the error occurred even when I didn't perform the per-tick cache though 😕 and also for creeps that were in the middle of the room (the last time the burst happened).  I've removed the cache to be safe though and try to repro.



  • FYI, I just got this error again, new stack trace and code:

    2Error Finding sources: RangeError: Maximum call stack size exceeded
    at RegExp.[Symbol.match] (native)
    at String.match (native)
    at Object.exports.roomNameToXY (/opt/engine/dist/utils.js:343:22)
    at Object.findRoute (/opt/engine/dist/game/map.js:91:46)
    at Object.findExit (/opt/engine/dist/game/map.js:236:30)
    at Room.findExitTo (/opt/engine/dist/game/rooms.js:1032:29)
    at Room.wrappedFunction [as findExitTo] (profiler:96:29)
    at RoomPosition.findPathTo (/opt/engine/dist/game/rooms.js:1137:32)
    at RoomPosition.wrappedFunction [as findPathTo] (profiler:96:29)
    at endNodes.objects.forEach.i (/opt/engine/dist/game/rooms.js:1224:93)

    var droppedEnergy = /*creep.room.droppedEnergy ||*/ creep.room.find(FIND_DROPPED_ENERGY, {filter: (source) => source.energy > 0});
    //creep.room.droppedEnergy = droppedEnergy;
    if (!!droppedEnergy.length && !!!sources.length && !!mustFillCarry) {
    sources = _.filter(droppedEnergy, (source) => source.energy > (creep.carryCapacity - creep.carry.energy));
    }
    if (!!droppedEnergy.length && !!!sources.length && !!!mustFillCarry) {
    sources = droppedEnergy;
    }

    var targetSource;
    try {
    targetSource = !!sources.length && creep.pos.findClosestByPath(sources, {maxOps: 2000});
    } catch (err) {
    console.log("role.transport: Error Finding sources: " + sources.length);
    try {
    targetSource = !!sources.length && creep.pos.findClosestByPath([sources[0]], {maxOps: 2000});
    } catch (e) {
    Game.notify("role.tarnsport test" + e.stack);
    console.log("role.transport: 2Error Finding sources: " + e.stack);
    }
    }



  • Ok, I did a little more digging and I believe it's something to do with the objects returned via room.find(FIND_DROPPED_ENERGY).

     

    I'm unable to JSON.stringify these objects due to them having a circular structure, so I wrote the below code to convert them to roomPosition objects only to search using findClosestByPath.  Suddenly the errors went away completely, reverting my code brought it back.  I was able to reliably reproduce via at least one location of dropped energy in the room.

     

    Code to work around bug:

    var newSources = [];
    for (var i = 0; i < sources.length; i++) {
    var pos = sources[i].pos;
    newSources.push(creep.room.getPositionAt(pos.x, pos.y));
    }

    sources = newSources;


  • Whats wrong with

     newSources.push(sources[i].pos);

    You can also stringify and log sources[i].pos. 

    IF the source IS in another room, you are simply setting the current room name to a pos of an other room, but thats not correct. 

    The stack trace shows that it tries to find the path to the exit, so it indeed may be in another room... 

     at Object.findExit (/opt/engine/dist/game/map.js:236:30)
     at Room.findExitTo (/opt/engine/dist/game/rooms.js:1032:29)

    You could do something like

    if ( sources[i].pos.roomName == creep.pos.roomName ) 
      newSources.push(sources[i].pos);

     



  • The source shouldn't be in another room as I'm doing a room.find, but just in case I went ahead and did as suggested.  I mostly did it at first because I wasn't sure which part of the result was circular and just wanted to create a new object.  The code is still working around the bug though.


    It seems that the objects returned by FIND_DROPPED_ENERGY is circular causing issues with path finding and logging it.