{"record":{"id":"4d11f7561d806e88","repo":"doctrine/orm","slug":"no-alias-was-set-before-invoking-getrootalias","errorCode":null,"errorMessage":"No alias was set before invoking getRootAlias().","messagePattern":"No alias was set before invoking getRootAlias\\(\\)\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/QueryBuilder.php","lineNumber":356,"sourceCode":"    /**\n     * Finds the root entity alias of the joined entity.\n     *\n     * @param string $alias       The alias of the new join entity\n     * @param string $parentAlias The parent entity alias of the join relationship\n     */\n    private function findRootAlias(string $alias, string $parentAlias): string\n    {\n        if (in_array($parentAlias, $this->getRootAliases(), true)) {\n            $rootAlias = $parentAlias;\n        } elseif (isset($this->joinRootAliases[$parentAlias])) {\n            $rootAlias = $this->joinRootAliases[$parentAlias];\n        } else {\n            // Should never happen with correct joining order. Might be\n            // thoughtful to throw exception instead.\n            $aliases = $this->getRootAliases();\n\n            if (! isset($aliases[0])) {\n                throw new RuntimeException('No alias was set before invoking getRootAlias().');\n            }\n\n            $rootAlias = $aliases[0];\n        }\n\n        $this->joinRootAliases[$alias] = $rootAlias;\n\n        return $rootAlias;\n    }\n\n    /**\n     * Gets the FIRST root alias of the query. This is the first entity alias involved\n     * in the construction of the query.\n     *\n     * <code>\n     * $qb = $em->createQueryBuilder()\n     *     ->select('u')\n     *     ->from('User', 'u');","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/QueryBuilder.php#L338-L374","documentation":"When QueryBuilder::join()/innerJoin()/leftJoin() adds a join, findRootAlias() maps the new join to a root alias: the parent alias if it is a root, else an earlier mapped join root, else the first root alias. This RuntimeException means none of those exist because the builder has no FROM/root alias at all, so the join cannot be attributed to a root.","triggerScenarios":"Calling $qb->leftJoin('u.address', 'a') on a builder where from(User::class, 'u') was never called (including $em->createQueryBuilder()->select('u') with no from, or after resetDQLPart('from')); calling add('join', ...) on a fresh builder; joining against a parent alias that was never added.","commonSituations":"Dynamic repository builders that add joins before the root; filter code that appends joins to whatever builder it receives; builder-merge utilities that copy join parts before the from part.","solutions":["Declare the root first: $qb->from(User::class, 'u')->leftJoin('u.address', 'a').","In dynamic code, guard join additions: if ($qb->getRootAliases() === []) add the from part before joining.","When copying parts between builders, copy 'from' before 'join'.","After resetDQLPart('from'), re-add from() before any further join() or add('join', ...)."],"exampleFix":"// before\n$qb = $em->createQueryBuilder();\n$qb->leftJoin('u.address', 'a'); // RuntimeException: no root alias yet\n\n// after\n$qb = $em->createQueryBuilder();\n$qb->from(User::class, 'u')->leftJoin('u.address', 'a');","handlingStrategy":"validation","validationCode":"if ($qb->getRootAliases() === []) {\n    throw new LogicException('Cannot join: QueryBuilder has no root alias. Call from() first.');\n}\n$qb->leftJoin('u.address', 'a');","typeGuard":null,"tryCatchPattern":"Catch \\Doctrine\\ORM\\RuntimeException only at boundaries that accept third-party builders, and rewrap with context: catch (RuntimeException $e) { throw new LogicException('Join added before root alias: ' . $joinDql, 0, $e); }","preventionTips":["Begin every builder chain with select()/from() before any join().","In shared filter helpers, assert count($qb->getRootAliases()) > 0 before appending joins.","When cloning or merging builders, copy parts in order: from, then join."],"tags":["doctrine-orm","query-builder","join","dql"],"backgroundTag":"missing-root-alias","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}