{"record":{"id":"666a307fcb4f771d","repo":"octobercms/october","slug":"too-much-recursion-check-for-circular-dependencie","errorCode":null,"errorMessage":"Too much recursion! Check for circular dependencies in your plugins.","messagePattern":"Too much recursion! Check for circular dependencies in your plugins\\.","errorType":"exception","errorClass":"SystemException","httpStatus":null,"severity":"critical","filePath":"modules/system/classes/PluginManager.php","lineNumber":1038,"sourceCode":"    }\n\n    /**\n     * sortDependencies sorts a collection of plugins, in the order that they should be actioned,\n     * according to their given dependencies. Least dependent come first.\n     * @return array Collection of sorted plugin identifiers\n     */\n    protected function sortDependencies()\n    {\n        ksort($this->plugins);\n\n        // Canvas the dependency tree\n        $checklist = $this->plugins;\n        $result = [];\n\n        $loopCount = 0;\n        while (count($checklist)) {\n            if (++$loopCount > 2048) {\n                throw new SystemException('Too much recursion! Check for circular dependencies in your plugins.');\n            }\n\n            foreach ($checklist as $code => $plugin) {\n                // Get dependencies and remove any aliens\n                $depends = $this->getDependencies($plugin) ?: [];\n                $depends = array_filter($depends, function ($pluginCode) {\n                    return isset($this->plugins[$pluginCode]);\n                });\n\n                // No dependencies\n                if (!$depends) {\n                    array_push($result, $code);\n                    unset($checklist[$code]);\n                    continue;\n                }\n\n                // Find dependencies that have not been checked\n                $depends = array_diff($depends, $result);","sourceCodeStart":1020,"sourceCodeEnd":1056,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/system/classes/PluginManager.php#L1020-L1056","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect `public $require` in plugins/*/*/Plugin.php for every recently added/updated plugin and remove one direction of the cycle","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","Remove the plugin entirely with `php artisan plugin:remove Author.Plugin` once the CMS is reachable","After fixing, run `php artisan cache:clear` and reload so the sorted plugin list is rebuilt"],"exampleFix":"// before — plugins/Acme/Blog/Plugin.php\npublic $require = ['Acme.Forum'];\n// ...while plugins/Acme/Forum/Plugin.php\npublic $require = ['Acme.Blog'];  // circular — boom: \"Too much recursion!\"\n// after — keep requires one-directional\n// plugins/Acme/Forum/Plugin.php\npublic $require = [];  // remove the back-dependency","handlingStrategy":"validation","validationCode":"// Detect circular $require chains before enabling a new plugin\nfunction hasCircularRequires(array $plugins): bool // code => (array) $require\n{\n    $graph = [];\n    foreach ($plugins as $code => $requires) {\n        $graph[$code] = array_values(array_intersect((array) $requires, array_keys($plugins)));\n    }\n    foreach (array_keys($graph) as $start) {\n        $seen = [];\n        $stack = [$start];\n        while ($stack) {\n            $node = array_pop($stack);\n            if (isset($seen[$node])) return true;\n            $seen[$node] = true;\n            foreach ($graph[$node] ?? [] as $next) $stack[] = $next;\n        }\n    }\n    return false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep $require strictly one-directional: dependencies must never require their dependents","Before installing a plugin pair, grep their Plugin.php $require arrays for mutual references","Never list the plugin's own code in $require","When the site is down from this error, rename the offending plugin dir to boot and fix"],"tags":["octobercms","plugin","circular-dependency","recursion","boot"],"backgroundTag":"circular-dependency","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}