What just happened?
You called a function, and PHP killed the request with this:
PHP Fatal error: Maximum function nesting level of '100' reached, aborting! in /var/www/html/app.php on line 15
PHP has a built-in safety cap on how deep function calls can nest. When your code recurses too deeply โ or when Xdebug lowers that cap โ PHP throws this fatal error and stops execution completely. No exceptions are thrown. No try/catch can save you.
Two root causes, different fixes
Before touching anything, figure out which case you're in:
- Case A: Xdebug is installed and the limit is 100 โ Xdebug sets its own nesting limit (default: 100), overriding PHP's native limit. Your code may be perfectly fine โ Xdebug just catches it first.
- Case B: Actual infinite recursion in your code โ A function keeps calling itself (or a cycle of functions calls each other) with no exit condition. The limit of 100 is just where PHP gave up.
Step 1 โ Check if Xdebug is the trigger
Run this in your terminal:
php -m | grep xdebug
Seeing xdebug in the output means it's loaded. Check its nesting limit:
php -r "echo ini_get('xdebug.max_nesting_level');"
Output of 100 (or any low number)? Xdebug is the culprit โ not your code.
Quick fix for Xdebug (development only)
Raise Xdebug's nesting limit in your php.ini or a separate xdebug.ini:
[xdebug]
xdebug.max_nesting_level = 512
Not sure which ini file to edit? Run:
php --ini
Then restart PHP-FPM or Apache:
# PHP-FPM
sudo systemctl restart php8.1-fpm
# Apache with mod_php
sudo systemctl restart apache2
512 is enough for legitimate deep recursion โ traversing a tree with 200 nested nodes, for example. Don't go to 9999: real infinite recursion will just eat all your memory instead of giving you a clear error.
Step 2 โ Find the infinite recursion in your code
No Xdebug, or raising the limit didn't help? You've got actual runaway recursion. Here's how to track it down.
Add a depth counter to suspect functions
Temporarily add a depth guard to any recursive function:
<?php
function processNode(array $node, int $depth = 0): void {
if ($depth > 50) {
throw new RuntimeException('Recursion too deep at node: ' . $node['id']);
}
// ... your logic ...
foreach ($node['children'] as $child) {
processNode($child, $depth + 1);
}
}
Now you get a real stack trace with the exact node ID causing the cycle โ not just a blind fatal error with no context.
Common recursion bugs and their fixes
Bug 1: Missing base case
// BROKEN โ no base case
function factorial(int $n): int {
return $n * factorial($n - 1); // never stops
}
// FIXED
function factorial(int $n): int {
if ($n $this->id,
'parent' => $this->parent->toArray(), // recurses into parent, then its parent...
'children' => array_map(fn($c) => $c->toArray(), $this->children),
];
}
}
// FIXED โ break the cycle by serializing IDs, not full objects
public function toArray(): array {
return [
'id' => $this->id,
'parent_id' => $this->parent->id, // ID only
'children' => array_map(fn($c) => $c->id, $this->children), // IDs only
];
}
Bug 3: Magic method loop (__get / __call)
// __get calling itself indirectly
class Config {
public function __get(string $key): mixed {
return $this->$key; // triggers __get again โ infinite loop
}
}
// FIXED โ access the backing store directly
class Config {
private array $data = [];
public function __get(string $key): mixed {
return $this->data[$key] ?? null;
}
}
Step 3 โ Use PHP's call stack to trace the loop
Can't spot the bug just by reading the code? Dump the call stack right before the crash:
<?php
function myRecursiveFunction(array $data): void {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
if (count($trace) > 8) {
echo "<pre>";
debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
echo "</pre>";
exit;
}
// ... rest of function
}
This prints the last 10 frames before you hit the limit, showing exactly where the cycle started.
Permanent fix: rewrite deep recursion as iteration
In production, the right move is often to ditch recursion entirely. Rewrite the function as a loop with an explicit stack โ no nesting limit, no memory surprise on large datasets:
<?php
// Recursive version โ hits nesting limit on large trees
function sumTree(array $node): int {
$total = $node['value'];
foreach ($node['children'] as $child) {
$total += sumTree($child);
}
return $total;
}
// Iterative version โ no recursion limit
function sumTreeIterative(array $root): int {
$stack = [$root];
$total = 0;
while (!empty($stack)) {
$node = array_pop($stack);
$total += $node['value'];
foreach ($node['children'] as $child) {
$stack[] = $child;
}
}
return $total;
}
Verify the fix worked
- Reload the page or re-run the failing script.
- Check your PHP error log โ the fatal should be gone:
tail -f /var/log/php/error.log
or for PHP-FPM:
tail -f /var/log/php8.1-fpm.log
- Raised Xdebug's limit? Confirm it took effect:
```
php -r "echo ini_get('xdebug.max_nesting_level');"
You should see `512` (or whatever value you set).
- Rewrote to iteration? Run a quick sanity check with a known input:
$tree = ['value' => 1, 'children' => [ ['value' => 2, 'children' => []], ['value' => 3, 'children' => []], ]]; echo sumTreeIterative($tree); // should print 6
## Quick reference
- **Xdebug installed + limit is 100** โ raise `xdebug.max_nesting_level` in php.ini
- **No Xdebug, real recursion bug** โ add a depth guard, use `debug_backtrace()` to find the cycle
- **Production code with deep trees** โ convert recursion to an explicit stack loop
- **ORM circular references** โ serialize IDs instead of full objects

