{"record":{"id":"c28869605db06cbd","repo":"laravel/framework","slug":"circular-alias-reference-for-abstract","errorCode":null,"errorMessage":"Circular alias reference for [{$abstract}].","messagePattern":"Circular alias reference for \\[(.+?)\\]\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Container/Container.php","lineNumber":1681,"sourceCode":"    {\n        return $this->bindings;\n    }\n\n    /**\n     * Get the alias for an abstract if available.\n     *\n     * @param  string  $abstract\n     * @return string\n     *\n     * @throws \\LogicException\n     */\n    public function getAlias($abstract)\n    {\n        $seen = [];\n\n        while (isset($this->aliases[$abstract])) {\n            if (isset($seen[$abstract])) {\n                throw new LogicException(\"Circular alias reference for [{$abstract}].\");\n            }\n\n            $seen[$abstract] = true;\n\n            $abstract = $this->aliases[$abstract];\n        }\n\n        return $abstract;\n    }\n\n    /**\n     * Get the extender callbacks for a given type.\n     *\n     * @param  string  $abstract\n     * @return array\n     */\n    protected function getExtenders($abstract)\n    {","sourceCodeStart":1663,"sourceCodeEnd":1699,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Container/Container.php#L1663-L1699","documentation":"LogicException from getAlias() when following the alias chain enters a cycle (e.g. A aliases to B which aliases back to A). The method walks $this->aliases tracking seen nodes and aborts rather than looping forever.","triggerScenarios":"Calling alias(A, B) then alias(B, A); multiple providers each registering aliases that together form a loop; an alias pointing to a key that is itself aliased back through configuration.","commonSituations":"Two service providers aliasing the same names in opposite directions; refactoring that swaps abstract and alias and leaves the old alias in place; config-driven alias generation producing symmetric entries.","solutions":["Break the cycle by removing one of the symmetric alias() calls.","Audit all alias() registrations (including package providers) for the names in the error and eliminate the loop.","Ensure aliases always point toward the concrete/abstract, not back to another alias.","Use getAlias() defensively in tests to detect cycles early during provider boot."],"exampleFix":"// before\n$app->alias('cache', CacheManager::class);\n$app->alias(CacheManager::class, 'cache'); // cycle\n// after\n$app->alias(CacheManager::class, 'cache'); // single direction: abstract -> short alias","handlingStrategy":"validation","validationCode":"// Detect alias cycles before they trigger at runtime.\nfunction aliasCycleExists(array $aliases, string $start): bool\n{\n    $seen = [];\n    $cur = $start;\n    while (isset($aliases[$cur])) {\n        if (isset($seen[$cur])) return true;\n        $seen[$cur] = true;\n        $cur = $aliases[$cur];\n    }\n    return false;\n}\n\n// build $aliases from your providers, then assert no cycle\nforeach (array_keys($aliases) as $k) {\n    if (aliasCycleExists($aliases, $k)) throw new RuntimeException(\"Alias cycle at [$k]\");\n}","typeGuard":"function aliasesAreAcyclic(array $aliases): bool\n{\n    foreach (array_keys($aliases) as $start) {\n        $seen = []; $cur = $start;\n        while (isset($aliases[$cur])) {\n            if (isset($seen[$cur])) return false;\n            $seen[$cur] = true; $cur = $aliases[$cur];\n        }\n    }\n    return true;\n}","tryCatchPattern":"try {\n    $container->alias($abstract, $alias);\n    $container->getAlias($alias);\n} catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'Circular alias')) {\n        // remove the reverse alias() that closed the loop\n    }\n    throw $e;\n}","preventionTips":["Register each alias exactly once and in one direction (abstract -> short key).","Audit package providers for aliases that conflict with your app aliases.","Add a boot-time assertion that getAlias() resolves for every registered alias without throwing."],"tags":["container","alias","configuration"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}