{"record":{"id":"80c224c29dbb88eb","repo":"doctrine/orm","slug":"no-alias-was-set-before-invoking-add","errorCode":null,"errorMessage":"No alias was set before invoking add().","messagePattern":"No alias was set before invoking add\\(\\)\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/QueryBuilder.php","lineNumber":653,"sourceCode":"        if (is_array($dqlPart) && $dqlPartName !== 'join') {\n            $dqlPart = reset($dqlPart);\n        }\n\n        if ($dqlPartName === 'join') {\n            $newDqlPart = [];\n\n            foreach ($dqlPart as $k => $v) {\n                if (is_numeric($k)) {\n                    Deprecation::trigger(\n                        'doctrine/orm',\n                        'https://github.com/doctrine/orm/pull/12051',\n                        'Using numeric keys in %s for join parts is deprecated and will not be supported in 4.0. Use an associative array with the root alias as key instead.',\n                        __METHOD__,\n                    );\n                    $aliases = $this->getRootAliases();\n\n                    if (! isset($aliases[0])) {\n                        throw new RuntimeException('No alias was set before invoking add().');\n                    }\n\n                    $k = $aliases[0];\n                }\n\n                $newDqlPart[$k] = $v;\n            }\n\n            $dqlPart = $newDqlPart;\n        }\n\n        if ($append && $isMultiple) {\n            if (is_array($dqlPart)) {\n                $key = key($dqlPart);\n\n                $this->dqlParts[$dqlPartName][$key][] = $dqlPart[$key];\n            } else {\n                $this->dqlParts[$dqlPartName][] = $dqlPart;","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/QueryBuilder.php#L635-L671","documentation":"add('join', $parts) expects an array keyed by the alias the joins belong to; numeric keys are deprecated and get remapped to the builder's first root alias. That remapping throws RuntimeException when the builder has no root aliases at all, because there is no alias to attribute the joins to.","triggerScenarios":"$qb->add('join', [$joinExpr]) (numeric list) on a builder with no from() yet; copying one builder's join parts into a fresh builder whose root has not been added; numeric-string keys like '0' also match the is_numeric remap path and emit the deprecation notice.","commonSituations":"Builder merge/clone utilities moving getDQLPart('join') arrays between builders; code written against ORM 2's numeric join arrays; filter bundles appending joins before the base query assembles its from part.","solutions":["Pass the associative form keyed by the root alias: $qb->add('join', ['u' => $joinExpr]).","Call from(User::class, 'u') before add('join', ...) so even the deprecated numeric form has a root to map to.","When copying parts between builders, copy 'from' first, then 'join'."],"exampleFix":"// before\n$target->add('join', [$sourceJoinExpr]); // numeric key + no root alias -> RuntimeException\n\n// after\n$target->from(User::class, 'u');\n$target->add('join', ['u' => $sourceJoinExpr]);","handlingStrategy":"validation","validationCode":"if ($qb->getRootAliases() === []) {\n    throw new LogicException(\"Add from() before add('join', ...): joins need a root alias.\");\n}\n$qb->add('join', ['u' => $joinExpr]); // associative key avoids the deprecated numeric path","typeGuard":null,"tryCatchPattern":"Catch \\Doctrine\\ORM\\RuntimeException in generic part-copy code and rethrow, including both builders' root aliases in the message.","preventionTips":["Always key join arrays by the root alias, never by index.","Copy 'from' before 'join' when merging builders.","Watch for the numeric-key deprecation notice: it is the early warning for this exception."],"tags":["doctrine-orm","query-builder","join","deprecated","dql-parts"],"backgroundTag":"missing-root-alias","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}