{"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/bd6b5437e6ad87bb49f9b426724f07a9f64e9683/src/Illuminate/Container/Container.php#L1663-L1699","documentation":"Thrown by Container::getAlias when following the alias chain produces a cycle: while resolving aliases, the same abstract is encountered twice, indicating the alias map points back to itself transitively (A -> B -> A). Without this guard getAlias would loop infinitely.","triggerScenarios":"Registering $container->alias('A','B') then $container->alias('B','A'), or any sequence where aliases form a cycle; resolving any abstract that hits the cyclic chain. Note: the direct self-alias case (A->A) is caught earlier by the 'aliased to itself' guard; this catches A->B->A and longer loops.","commonSituations":"Programmatic alias setup from config that accidentally creates a loop; renaming classes and updating aliases in two places that now reference each other; service provider ordering producing cross-referential aliases.","solutions":["Inspect all alias() calls and break the cycle so the chain terminates at a non-aliased abstract.","Use $container->getAlias($abstract) in a debug dump to find which abstract closes the loop.","Register aliases in a single canonical place (e.g. one service provider) to prevent conflicting registrations."],"exampleFix":"// before\n$app->alias(Contract::class, 'payment');\n$app->alias('payment', Contract::class); // back-reference\n\n// after\n$app->alias(Contract::class, 'payment');\n// remove the reverse alias; resolve via either 'payment' or Contract::class","handlingStrategy":"validation","validationCode":"function aliasChainTerminates(\\Illuminate\\Container\\Container $c, string $start, int $max = 50): bool\n{\n    $seen = []; $cur = $start;\n    while (isset($c->getAliases()[$cur] ?? null) || $c->isAlias($cur)) {\n        if (isset($seen[$cur])) return false;\n        $seen[$cur] = true; $cur = $c->getAlias($cur);\n        if (--$max < 0) return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    $container->getAlias($abstract);\n} catch (\\LogicException $e) {\n    if (str_contains($e->getMessage(), 'Circular alias')) {\n        // break the cycle by re-registering a single canonical alias\n    }\n    throw $e;\n}","preventionTips":["Register all aliases from one config array validated for cycles.","Avoid bidirectional alias() calls.","Log alias registrations during boot for audit."],"tags":["container","alias","configuration","di","infinite-loop"],"analyzedSha":"bd6b5437e6ad87bb49f9b426724f07a9f64e9683","analyzedAt":"2026-08-06T00:28:32.783Z","schemaVersion":2}