{"record":{"id":"6fd4322cb3d0bd48","repo":"phalcon/cphalcon","slug":"circular-alias-detected-alias","errorCode":null,"errorMessage":"Circular alias detected: '{alias}'","messagePattern":"Circular alias detected: '(.+?)'","errorType":"exception","errorClass":"Phalcon\\Container\\Exceptions\\CircularAliasFound","httpStatus":null,"severity":"error","filePath":"phalcon/Container/Container.zep","lineNumber":665,"sourceCode":"    /**\n     * Detect circular aliases\n     *\n     * @param string $alias\n     * @param string $target\n     *\n     * @return void\n     * @throws CircularAliasFound\n     */\n    private function detectCircularAlias(string alias, string target) -> void\n    {\n        var current, seen;\n\n        let current = target;\n        let seen    = [];\n\n        while (true) {\n            if (current === alias) {\n                throw new CircularAliasFound(alias);\n            }\n\n            if (array_key_exists(current, seen)) {\n                break;\n            }\n\n            if (!array_key_exists(current, this->aliases)) {\n                break;\n            }\n\n            let seen[current] = true;\n            let current       = this->aliases[current];\n        }\n    }\n\n    /**\n     * Locate a processor\n     *","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Container/Container.zep#L647-L683","documentation":"setAlias(name, alias) walks the existing alias chain starting from the target to verify the new alias cannot loop back onto itself; CircularAliasFound is thrown when following the chain returns to the new alias. Cycles are rejected because alias resolution would never terminate.","triggerScenarios":"setAlias('db', 'database') followed by setAlias('database', 'db') (two-way alias); longer cycles a->b->c->a introduced one link at a time; a self-alias setAlias('db', 'db').","commonSituations":"Two modules each registering a friendly name that points at the other; copy-pasted alias setup where direction was flipped; refactors that add a reverse alias without removing the old one.","solutions":["Keep aliases one-directional — every alias chain must terminate at a real registered service name","Remove the old reverse mapping before adding a new alias in the opposite direction (rebuild the alias set cleanly)","Sketch or assert the chain before registering; check with hasAlias() to catch duplicates","Never self-alias: setAlias('x', 'x') is always a cycle"],"exampleFix":"// before\n$c->setAlias('db', 'database');\n$c->setAlias('database', 'db'); // CircularAliasFound\n\n// after\n$c->set('db', Pdo\\Connection::class);\n$c->setAlias('db', 'database'); // database -> db (terminates)","handlingStrategy":"validation","validationCode":"// Pre-validate that adding alias -> target keeps the graph acyclic\nfunction aliasWouldCycle(array $aliases, string $alias, string $target): bool\n{\n    $current = $target;\n    while (isset($aliases[$current])) {\n        if ($current === $alias) {\n            return true;\n        }\n        $current = $aliases[$current];\n    }\n    return false;\n}\n\nif (!aliasWouldCycle($aliasMap, 'database', 'db')) {\n    $container->setAlias('db', 'database');\n}","typeGuard":null,"tryCatchPattern":"use Phalcon\\Container\\Exceptions\\CircularAliasFound;\n\ntry {\n    $container->setAlias('database', 'db');\n} catch (CircularAliasFound $e) {\n    // log and abort bootstrap: alias chain loops, fix the mapping table\n}","preventionTips":["Maintain aliases as a flat declarative map (alias => final service) validated at boot, never bidirectional","Every alias chain must end at a real service name; draw the chain before adding links","Never self-alias or add reverse aliases after refactors"],"tags":["di-container","alias","circular-reference","phalcon"],"backgroundTag":"circular-reference-detected","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}