{"record":{"id":"266249181674e579","repo":"doctrine/orm","slug":"cannot-count-query-which-selects-two-from-componen-266249","errorCode":null,"errorMessage":"Cannot count query which selects two FROM components, cannot make distinction","messagePattern":"Cannot count query which selects two FROM components, cannot make distinction","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Tools/Pagination/WhereInWalker.php","lineNumber":51,"sourceCode":"class WhereInWalker extends TreeWalkerAdapter\n{\n    /**\n     * ID Count hint name.\n     */\n    public const HINT_PAGINATOR_HAS_IDS = 'doctrine.paginator_has_ids';\n\n    /**\n     * Primary key alias for query.\n     */\n    public const PAGINATOR_ID_ALIAS = 'dpid';\n\n    public function walkSelectStatement(SelectStatement $selectStatement): void\n    {\n        // Get the root entity and alias from the AST fromClause\n        $from = $selectStatement->fromClause->identificationVariableDeclarations;\n\n        if (count($from) > 1) {\n            throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');\n        }\n\n        $fromRoot            = reset($from);\n        $rootAlias           = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;\n        $rootClass           = $this->getMetadataForDqlAlias($rootAlias);\n        $identifierFieldName = $rootClass->getSingleIdentifierFieldName();\n\n        $pathType = PathExpression::TYPE_STATE_FIELD;\n        if (isset($rootClass->associationMappings[$identifierFieldName])) {\n            $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;\n        }\n\n        $pathExpression       = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName);\n        $pathExpression->type = $pathType;\n\n        $hasIds = $this->_getQuery()->getHint(self::HINT_PAGINATOR_HAS_IDS);\n\n        if ($hasIds) {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Tools/Pagination/WhereInWalker.php#L33-L69","documentation":"WhereInWalker is the Paginator's tree walker that rewrites the paginated query into 'SELECT ... WHERE root.id IN (:dpid)'. It derives the root entity from the first FROM declaration; with two or more identification variable declarations there is no single root to filter on, so it refuses. The identical guard exists in LimitSubqueryOutputWalker::getSQLIdentifier (line 517), so switching to output walkers does not make a multi-root query paginable - the query must have one root.","triggerScenarios":"Paginator count() or iteration (tree-walker path: setUseOutputWalkers(false) or a query-level custom output walker hint) over a DQL with more than one FROM component such as 'SELECT u, a FROM App\\Entity\\User u, App\\Entity\\Address a WHERE ...'. In output-walker mode the same multi-root query throws the identical message from LimitSubqueryOutputWalker.","commonSituations":"Theta-style comma joins left over from legacy DQL; repository queries cross-joining an unrelated entity for filtering; attempts to paginate queries that select two entity types at once.","solutions":["Reduce the query to a single FROM component by converting the second root into an explicit JOIN ('JOIN App\\Entity\\Address a WITH a.user = u') or a WHERE EXISTS subquery.","Paginate the primary entity only, then load the secondary data for the page in a follow-up query.","Compute the total with a dedicated single-root COUNT query and fetch pages manually via setFirstResult/setMaxResults."],"exampleFix":"// before\n$dql = 'SELECT u, a FROM App\\Entity\\User u, App\\Entity\\Address a WHERE u.id = a.userId';\ncount(new Paginator($em->createQuery($dql))); // RuntimeException\n\n// after\n$dql = 'SELECT u FROM App\\Entity\\User u JOIN u.address a';\ncount(new Paginator($em->createQuery($dql)));","handlingStrategy":"validation","validationCode":"// Both count() and getIterator() require a single root; check before using Paginator\n$ast  = $query->getAST();\n$fromCount = count($ast->fromClause->identificationVariableDeclarations);\nif ($fromCount !== 1) {\n    throw new InvalidArgumentException(sprintf('Cannot paginate a query with %d FROM components', $fromCount));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write pagination DQL with one root plus explicit JOINs from day one","Add a repository-level guard/CI check rejecting theta-style FROM in paginated queries","Remember output walkers throw the same error - the only fix is rewriting the query"],"tags":["doctrine-orm","paginator","pagination","where-in-walker","dql","from-clause"],"backgroundTag":"pagination-multi-root-query","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}