Maximum Call Stack Size Exceeded Error

Maximum call stack size exceeded error

It means that somewhere in your code, you are calling a function which in turn calls another function and so forth, until you hit the call stack limit.

This is almost always because of a recursive function with a base case that isn't being met.

Viewing the stack

Consider this code...

(function a() {
a();
})();

Here is the stack after a handful of calls...

Web Inspector

As you can see, the call stack grows until it hits a limit: the browser hardcoded stack size or memory exhaustion.

In order to fix it, ensure that your recursive function has a base case which is able to be met...

(function a(x) {
// The following condition
// is the base case.
if ( ! x) {
return;
}
a(--x);
})(10);

I am getting RangeError: Maximum call stack size exceeded error in Javascript

As you wrote yourself, "Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle".

In your implementation, fn calls itself infinitely. In the process, the javascript virtual machine creates a fonction context in memory at each iteration. This can be done up to a certain times after which the error Maximum call stack size exceeded throws.

What I am doing wrong?

You return true when you find a happy number, but you never return false otherwise. Add a condition to detect unhappy numbers (detect a cycle) and return false in that case.

Edit: here is an example implementation:

var isHappy = function(n) {
if(n.length<2) return false

// placeholder to store already called values
const calledValues= new Set()

var fn=(n)=>{
let i=0;
let sum=0;

if (calledValues.has(n))
// cycle detected!
return false;

calledValues.add(n);

while(i<n.length){
sum=sum+Math.pow(n[n.length-1-i],2);
i++;
}

if (sum !== 1)
return fn(sum.toString());
else
// sum === 1, number is happy!
return true
}

// make sure to pass a string to fn
return fn(n.toString());
};

(new Array(20)).fill().map((_,i)=>10+i)
.forEach(n=> console.log(n, "is happy?", isHappy(n)));

Uncaught RangeError: Maximum call stack size exceeded - WHY? / How to avoid?

This is a recursion issue. Your function is calling itself over and over again untill stack gets filled and causes the error shown.

If you remove these lines of code you will solve the error:

if(drawnItems.length != new Set(drawnItems).size) {
getObjects()
}

Is there any reason why you had that if condition to perform recursion?

Edit:

In order to avoid duplicated values you can use the following code to also remove the recursion:

//First object won't be duplicated, so we declare it directly
var randomObject1 = array[getRandomNumber(1, array.length -1)];

var randomObject2 = null, randomObject3 = null;

do {
randomObject2 = array[getRandomNumber(1, array.length -1)];
} while (randomObject2 == randomObject1);

do {
randomObject3 = array[getRandomNumber(1, array.length -1)];
} while (randomObject3 == randomObject1 || randomObject3 == randomObject2);

With the code above you'll keep generating random values for randomObject2 and randomObject3 while they have the same value as another.

Recursive Function Uncaught RangeError: Maximum call stack size exceeded

You end up having way too many recursive calls when the input value is large. It should be fairly straight forward to convert from recursive to iterative using a while loop. The only issue I ran into was floating point not getting down to 0 (like mentioned in the comment by @Keith). When doing money calculations, it is usually best to use integers. Use the smallest denomination in the currency. For example, in US currency, that would be cents. Only convert to decimal (or dollars in this case) when displaying the values.

I have also simplified your calculations a bit. Because of these changes, you could actually use recursion now, if you want since the maximum level of recursion now is predictorList.length.

let predictorList = [
10000, 5000, 2000, 1000, 500, 100, 50, 25, 10, 5, 1
];
let total = 709;

function isRemainingMoney(value) {
let returnList = [];

// when remaning value becomes zero then return the returnlist
while (value != 0) {
for (let pRed of predictorList) {
if (value >= pRed) {
returnList.push({
money: pRed / 100,
count: Math.floor(value / pRed),
});
value %= pRed;
}
}
}
return returnList;
}
document.querySelector('input').addEventListener('change', (event) => {
if (!!event.target.value) {
let returnList = isRemainingMoney(+event.target.value * 100 - total);
console.log(returnList, 'returnList');
}

})
<input type="number">

Maximum call stack size exceeded for large iterators

(V8 developer here.)

It's not about the iterator, it's about the RegExp.

[Update]

Looks like I was misled by a typo in my test, so my earlier explanation/suggestion doesn't fix the problem. With the test corrected, it turns out that only the end of the expression (which I called "fishy" before) needs to be fixed.

The massive consumption of stack memory is caused by the fact that (.|\n) is a capture group, and is matched very frequently. One idea would be to write it as [.\n] instead, but the . metacharacter is not valid inside [...] character sets.

Hat tip to @cachius for suggesting an elegant solution: use the s flag to make . match \n characters.

As an unrelated fix, prefer checking for the closing } instead of the next opening [ at the beginning of a line, so that there's no overlap between matched ranges (which would make you miss some matches).

So, in summary, replace ((.|\n)+?)\n\[/g with (.+?)\n}/gs.

[/Update]

Here is a reproducible example. The following exhibits the stack overflow:

let lines = ["[TIMESTAMP] [DEBUG] [Item ITEM_ID] {"];
for (let i = 0; i < 1000000; i++) {
lines.push(" [0]"); // Fake JSON, close enough for RegExp purposes.
}
lines.push("}");
lines.push("[TIMESTAMP]");
let myLongString = lines.join("\n");

const re = /\[(.+?)\] \[DEBUG\] \[Item (.+?)\] ((.|\n)+?)\n\[/g;
myLongString.match(re);

If you replace the const re = ... line with:

const re = /\[(.+?)\] \[DEBUG\] \[Item (.+?)\] (.+?)\n}/gs;

then the stack overflow disappears.

(It would be possible to reduce the simplified example even further, but then the connection with your original case wouldn't be as obvious.)

[Original post below -- the mechanism I explained here is factually correct, and applying the suggested replacement indeed improves performance by 25% because it makes the RegExp simpler to match, it just isn't enough to fix the stack overflow.]

The problematic pattern is:

\[(.+?)\]

which, after all, means "a [, then any number of arbitrary characters, then a ]". While I understand that regular expressions might seem like magic, they're actually real algorithmic work under the hood, kind of like miniature programs in their own right. In particular, any time a ] is encountered in the string, the algorithm has to decide whether to count this as one of the "arbitrary characters", or as the one ] that ends this sequence. Since it can't magically know that, it has to keep both possibilities "in mind" (=on the stack), pick one, and backtrack if that turns out to be incorrect. Since this backtracking information is kept on the stack (where else?), if you put sufficiently many ] into your string, the stack will run out of space.

Luckily, the solution is simple: since what you actually mean is "a [, then any number of characters that aren't ], then a ]", you can just tell the RegExp engine that, replacing . with [^\]]:

\[([^\]]+?)\]

Note: ((.|\n)+?)\n\[ seems fishy for the same reason, but according to this test doesn't appear to be the problem, even if I further increase the input size. I'm not sure why; it might be due to how I created the test. If you see further problems with the real input, it may be worth reformulating this part as well.

[/Original post]

Firebase Cloud Function - RangeError: Maximum call stack size exceeded

Finally got it!

The solution came from this answer.

In short; I needed to add a .then() onto the returned axios chain like so:

export const getZohoDeskTickets = functions
.region('us-central1')
.https.onCall(async (data, context) => {
// Check if it passed App Check
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.'
);
}

// Check the authentication
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError(
'unauthenticated',
'The function must be called while authenticated.'
);
}

// Do the operation
const zohoAccessToken = await getSecretVersion(
'zoho-self-client-api-access-token'
).catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
return axios
.get('https://desk.zoho.com/api/v1/tickets', {
headers: {
orgId: '774638961',
Authorization: `Zoho-oauthtoken ${zohoAccessToken}`,
},
})
.then((response) => {
functions.logger.info(response.data);
return response.data;
})
.catch((error) => {
functions.logger.error(error.response.data);
throw new functions.https.HttpsError(
'unknown',
error.response.data.message,
error.response.data
);
});
});

RangeError: Maximum call stack size exceeded. Why?

There are two problems here: 1. getting it to run, 2. getting it to run properly.

1.
You are incorrectly parsing data, such that let n = parseInt(qdata.n); gives you back undefined. The callback is the function that is executed once the server starts. This means that even before you have inputted a number, your server is running the fibonacci sequence.

Because it's already parsing data, it parses n, which, of course, is undefined. Parsing this into an Int, you get NaN. This is a problem because Nan-1 or Nan-2 always returns Nan, meaning that you enter into an infinite upon start of the server.

To fix this, you should check whether n exists:

if(String(n) !== String(NaN) {
...
}

  1. Efficiency: calculating the fibonacci sequence like this will cause a huge problems down the line. I would take a look at dynamic programming.


Related Topics



Leave a reply



Submit