Quick Answer

Infinite recursion or too many nested function calls. Add base cases to recursive functions, check for circular references, and limit recursion depth.

Understanding the Issue

Stack overflow errors happen when the JavaScript call stack exceeds its maximum size limit. This is most commonly caused by infinite recursion where a function calls itself without a proper base case to stop the recursion. Other causes include circular object references, very deep nested function calls, or mutual recursion between functions. Understanding recursion patterns and implementing proper safeguards prevents these errors.

The Problem

This code demonstrates the issue:

Javascript Error
// Problem 1: Infinite recursion - missing base case
function countdown(n) {
    console.log(n);
    countdown(n - 1); // Never stops - no base case
}

// Problem 2: Incorrect recursive condition
function factorial(n) {
    if (n > 0) { // Wrong condition
        return n * factorial(n - 1);
    }
    return 1;
}

// Problem 3: Circular function calls
function functionA() {
    console.log("Function A");
    functionB();
}

function functionB() {
    console.log("Function B");
    functionA(); // Circular call
}

// These will cause stack overflow:
// countdown(10);
// factorial(-1);
// functionA();

The Solution

Here's the corrected code:

Javascript Fixed
// Solution 1: Proper recursive function with base case
function countdown(n) {
    // Base case to stop recursion
    if (n <= 0) {
        console.log("Done!");
        return;
    }
    
    console.log(n);
    countdown(n - 1);
}

countdown(5); // Works correctly

// Solution 2: Correct factorial with proper conditions
function factorial(n) {
    // Handle edge cases
    if (n < 0) {
        throw new Error("Factorial not defined for negative numbers");
    }
    
    // Base cases
    if (n === 0 || n === 1) {
        return 1;
    }
    
    return n * factorial(n - 1);
}

console.log("Factorial 5:", factorial(5));

// Solution 3: Iterative alternative to recursion
function factorialIterative(n) {
    if (n < 0) {
        throw new Error("Factorial not defined for negative numbers");
    }
    
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

console.log("Iterative factorial 5:", factorialIterative(5));

// Solution 4: Recursion with depth limit
function safeRecursion(n, maxDepth = 1000, currentDepth = 0) {
    // Check depth limit
    if (currentDepth >= maxDepth) {
        throw new Error("Maximum recursion depth exceeded");
    }
    
    // Base case
    if (n <= 0) {
        return "Base case reached";
    }
    
    console.log(`Depth ${currentDepth}: ${n}`);
    return safeRecursion(n - 1, maxDepth, currentDepth + 1);
}

try {
    safeRecursion(10, 15); // Safe with limit
} catch (error) {
    console.error("Safe recursion error:", error.message);
}

// Solution 5: Handle circular references
function safeStringify(obj, maxDepth = 10) {
    const seen = new WeakSet();
    
    function replacer(key, value) {
        if (typeof value === "object" && value !== null) {
            if (seen.has(value)) {
                return "[Circular Reference]";
            }
            seen.add(value);
        }
        return value;
    }
    
    try {
        return JSON.stringify(obj, replacer, 2);
    } catch (error) {
        return `Error stringifying: ${error.message}`;
    }
}

// Test with circular objects
const obj1 = { name: "Object 1" };
const obj2 = { name: "Object 2" };
obj1.ref = obj2;
obj2.ref = obj1;

console.log("Safe stringify:", safeStringify(obj1));

Key Takeaways

Always include base cases in recursive functions. Add depth limits to prevent stack overflow. Use iterative solutions for deep recursion. Handle circular references in objects.