octobercms/october · critical · SystemException

Too much recursion! Check for circular dependencies in your

Error message

Too much recursion! Check for circular dependencies in your plugins.

What it means

PluginManager::sortDependencies() topologically sorts installed plugins by their $require declarations. It repeatedly pulls plugins whose dependencies are already resolved from a checklist; a hard cap of 2048 loop iterations guards against an infinite loop. When the cap is exceeded, no plugin in the remaining checklist ever reaches zero pending dependencies, which in practice means a circular require chain (A requires B while B requires A, directly or transitively, or a plugin requiring itself). The exception aborts plugin registration, so the whole application fails to boot.

Source

Thrown at modules/system/classes/PluginManager.php:1038

    }

    /**
     * sortDependencies sorts a collection of plugins, in the order that they should be actioned,
     * according to their given dependencies. Least dependent come first.
     * @return array Collection of sorted plugin identifiers
     */
    protected function sortDependencies()
    {
        ksort($this->plugins);

        // Canvas the dependency tree
        $checklist = $this->plugins;
        $result = [];

        $loopCount = 0;
        while (count($checklist)) {
            if (++$loopCount > 2048) {
                throw new SystemException('Too much recursion! Check for circular dependencies in your plugins.');
            }

            foreach ($checklist as $code => $plugin) {
                // Get dependencies and remove any aliens
                $depends = $this->getDependencies($plugin) ?: [];
                $depends = array_filter($depends, function ($pluginCode) {
                    return isset($this->plugins[$pluginCode]);
                });

                // No dependencies
                if (!$depends) {
                    array_push($result, $code);
                    unset($checklist[$code]);
                    continue;
                }

                // Find dependencies that have not been checked
                $depends = array_diff($depends, $result);

View on GitHub (pinned to b608633a7e)

Solutions

  1. Inspect `public $require` in plugins/*/*/Plugin.php for every recently added/updated plugin and remove one direction of the cycle
  2. Temporarily rename the offending plugin's directory (e.g. plugins/acme/blog -> plugins/acme/blog.disabled) so the site boots, then fix its $require and restore it
  3. Remove the plugin entirely with `php artisan plugin:remove Author.Plugin` once the CMS is reachable
  4. After fixing, run `php artisan cache:clear` and reload so the sorted plugin list is rebuilt

Example fix

// before — plugins/Acme/Blog/Plugin.php
public $require = ['Acme.Forum'];
// ...while plugins/Acme/Forum/Plugin.php
public $require = ['Acme.Blog'];  // circular — boom: "Too much recursion!"
// after — keep requires one-directional
// plugins/Acme/Forum/Plugin.php
public $require = [];  // remove the back-dependency
Defensive patterns

Strategy: validation

Validate before calling

// Detect circular $require chains before enabling a new plugin
function hasCircularRequires(array $plugins): bool // code => (array) $require
{
    $graph = [];
    foreach ($plugins as $code => $requires) {
        $graph[$code] = array_values(array_intersect((array) $requires, array_keys($plugins)));
    }
    foreach (array_keys($graph) as $start) {
        $seen = [];
        $stack = [$start];
        while ($stack) {
            $node = array_pop($stack);
            if (isset($seen[$node])) return true;
            $seen[$node] = true;
            foreach ($graph[$node] ?? [] as $next) $stack[] = $next;
        }
    }
    return false;
}

Prevention

When it happens

Trigger: Two or more installed plugins list each other in `public $require`; a plugin lists its own code in $require; a long require chain that can never satisfy itself because one member is missing-but-referenced in a way the filter keeps alive. Hit on any code path that boots the plugin manager: web request, artisan command, composer hook.

Common situations: Plugin authors adding mutual requires during co-development; a marketplace plugin updating to require a plugin that already requires it back; leftover dev-only requires shipped to production; a freshly cloned install pulling a new plugin with composer that closes a cycle.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/666a307fcb4f771d. Report an issue: GitHub.