Corriger l'erreur « Maximum call stack size exceeded » en JavaScript
Vous fixez votre console, et la voilà : RangeError: Maximum call stack size exceeded. Dans Firefox, le message peut être too much recursion. Dans tous les cas, votre application vient de planter, et vous devez comprendre pourquoi — et comment corriger cela correctement.
Cette erreur signale un débordement de pile : votre code a effectué tellement d’appels de fonctions imbriqués que le moteur JavaScript n’a plus d’espace pour les suivre. Explorons ce qui déclenche cela dans le JavaScript moderne, comment le déboguer efficacement, et comment le prévenir dans les applications React, Next.js et Node.js.
Points clés à retenir
- La pile d’appels a une taille finie qui varie selon l’environnement, donc n’écrivez jamais de code qui repose sur l’atteinte d’une limite spécifique.
- Les causes courantes incluent la récursion infinie, la récursion mutuelle, les re-rendus infinis dans React et les références circulaires lors de la sérialisation JSON.
- Déboguez en activant « Pause on Exceptions » dans les DevTools et en recherchant des motifs de fonctions répétitifs dans la pile d’appels.
- Corrigez structurellement en convertissant la récursion en itération, en utilisant le trampolining, ou en gérant les références circulaires avec une fonction de remplacement.
Ce que fait réellement la pile d’appels
Chaque fois qu’une fonction s’exécute, le moteur JavaScript crée un cadre de pile contenant les variables locales de la fonction, ses arguments et son adresse de retour. Ces cadres s’empilent les uns sur les autres. Quand une fonction retourne, son cadre est supprimé.
Le problème ? Cette pile a une taille finie. La limite exacte varie selon l’environnement — Chrome peut autoriser environ 10 000 à 15 000 cadres, tandis que Firefox en permet environ 50 000. Node.js plafonne généralement autour de 11 000 cadres par défaut.
Important : Ces chiffres dépendent de l’implémentation et peuvent changer entre les versions. N’écrivez pas de code qui repose sur l’atteinte d’une limite spécifique.
Schémas courants qui déclenchent un débordement de pile
Récursion infinie classique
Le cas d’école : une fonction qui s’appelle elle-même sans condition de sortie appropriée.
function processItem(item) {
// Cas de base manquant
return processItem(item.child)
}
Récursion mutuelle
Deux fonctions qui s’appellent mutuellement en boucle :
function isEven(n) {
return n === 0 ? true : isOdd(n - 1)
}
function isOdd(n) {
return n === 0 ? false : isEven(n - 1)
}
isEven(100000) // Débordement de pile
Re-rendus infinis dans React
C’est là que de nombreux développeurs frontend rencontrent l’erreur. Les mises à jour d’état pendant le rendu créent des boucles infinies :
function BrokenComponent() {
const [count, setCount] = useState(0)
setCount(count + 1) // Déclenche un re-rendu immédiatement
return <div>{count}</div>
}
De mauvaises dépendances useEffect causent des problèmes similaires :
useEffect(() => {
setData(transformData(data)) // data change, l'effet s'exécute à nouveau
}, [data])
Débordement de pile avec référence circulaire JSON
Quand des objets se référencent eux-mêmes, JSON.stringify récurse infiniment :
const obj = { name: 'test' }
obj.self = obj
JSON.stringify(obj) // Maximum call stack size exceeded
Déboguer le débordement de pile d’appels dans Node.js et les navigateurs
Étape 1 : Activer « Pause on Exceptions »
Dans Chrome DevTools, ouvrez le panneau Sources et activez « Pause on caught exceptions ». Pour Node.js, utilisez le flag --inspect et connectez Chrome DevTools.
Étape 2 : Inspecter la pile d’appels pour les cadres répétitifs
Quand le débogueur se met en pause, examinez le panneau de la pile d’appels. Recherchez des motifs répétitifs — la même fonction apparaissant des dizaines ou des centaines de fois indique votre point de récursion.
Étape 3 : Utiliser les traces de pile asynchrones
Les DevTools modernes affichent les traces de pile asynchrones par défaut. Cela aide quand la récursion s’étend sur des chaînes de Promise ou des callbacks setTimeout.
console.trace() // Affiche la trace de pile actuelle
Note : Augmenter la taille de la pile avec node --stack-size est un outil de diagnostic, pas une solution. Cela retarde le plantage mais ne corrige pas le bug.
Discover how at OpenReplay.com.
Corrections pratiques qui fonctionnent réellement
Convertir la récursion en itération
La plupart des algorithmes récursifs peuvent devenir itératifs avec une pile explicite :
function processTree(root) {
const stack = [root]
while (stack.length > 0) {
const node = stack.pop()
process(node)
if (node.children) {
stack.push(...node.children)
}
}
}
Utiliser le trampolining pour la récursion profonde
Les trampolines divisent la récursion en étapes, empêchant la croissance de la pile :
function trampoline(fn) {
return function(...args) {
let result = fn(...args)
while (typeof result === 'function') {
result = result()
}
return result
}
}
Gérer les références circulaires en toute sécurité
Pour la sérialisation JSON, utilisez une fonction de remplacement ou des bibliothèques comme flatted :
const seen = new WeakSet()
JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]'
}
seen.add(value)
}
return value
})
Prévenir la récursion infinie dans React
Assurez-vous toujours que les dépendances de useEffect sont stables et que les mises à jour d’état sont conditionnelles :
useEffect(() => {
if (!isProcessed) {
setData(transformData(rawData))
setIsProcessed(true)
}
}, [rawData, isProcessed])
Pourquoi l’optimisation des appels terminaux ne vous sauvera pas
ES6 spécifiait les appels terminaux appropriés (proper tail calls), qui permettraient théoriquement une récursion infinie en position terminale. En pratique, seul Safari implémente cela. Chrome et Firefox ne le font pas, et Node.js l’a désactivé. Ne comptez pas sur la TCO — refactorisez plutôt votre code.
Conclusion
L’erreur « Maximum call stack size exceeded » est un bug de haute gravité nécessitant des corrections structurelles. Vous ne pouvez pas vous en sortir avec un catch, et vous ne devriez pas essayer d’augmenter les limites de pile en production.
Trouvez le motif répétitif dans votre trace de pile, puis ajoutez des conditions de terminaison appropriées, convertissez en itération, ou divisez le travail en morceaux asynchrones. Traitez les références circulaires comme des problèmes de structure de données, pas comme des problèmes de sérialisation.
Quand vous voyez cette erreur, votre code vous dit que quelque chose de fondamental doit changer.
FAQ
Techniquement oui, mais ce n'est pas une solution fiable. Au moment où cette erreur est levée, votre application est dans un état instable. La pile est épuisée, et capturer l'erreur ne la restaure pas. Corrigez plutôt le problème de récursion sous-jacent au lieu d'essayer de gérer l'exception.
Ouvrez les DevTools de votre navigateur, allez dans le panneau Sources, et activez Pause on Exceptions. Quand l'erreur se produit, examinez le panneau de la pile d'appels pour les noms de fonctions répétitifs. La fonction qui apparaît de manière répétée est votre coupable. Vous pouvez aussi utiliser console.trace() pour afficher la pile à des points spécifiques.
Appeler setState directement dans le corps du rendu déclenche un re-rendu immédiat, qui appelle setState à nouveau, créant une boucle infinie. Déplacez les mises à jour d'état dans des hooks useEffect avec des dépendances appropriées, ou dans des gestionnaires d'événements. Ne mettez jamais à jour l'état de manière inconditionnelle pendant le rendu.
Dans Node.js, vous pouvez utiliser le flag --stack-size pour augmenter la limite, mais cela ne fait que retarder le plantage. Les navigateurs ne permettent pas de modifier la taille de la pile. Aucune approche ne corrige la cause racine. Refactorisez plutôt votre code pour utiliser l'itération ou des motifs asynchrones au lieu de compter sur une récursion profonde.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.