doctrine/orm · error · RuntimeException

Paginating an entity with foreign key as identifier only wor

Error message

Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.

What it means

The Paginator can paginate either via an output walker (LimitSubqueryOutputWalker, which writes wrapping SQL) or via AST tree walkers (LimitSubqueryWalker, which rewrites the SELECT clause to the identifier path expression). When the root entity's identifier is itself an association (foreign key used as primary key, e.g. #[Id] #[ManyToOne]), the tree walker cannot express 'u.association-id' as a plain state field, so it aborts. The message states the remedy: let the Paginator use output walkers.

Source

Thrown at src/Tools/Pagination/LimitSubqueryWalker.php:47

    /**
     * Counter for generating unique order column aliases.
     */
    private int $aliasCounter = 0;

    public function walkSelectStatement(SelectStatement $selectStatement): void
    {
        // Get the root entity and alias from the AST fromClause
        $from      = $selectStatement->fromClause->identificationVariableDeclarations;
        $fromRoot  = reset($from);
        $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;
        $rootClass = $this->getMetadataForDqlAlias($rootAlias);

        $this->validate($selectStatement);
        $identifier = $rootClass->getSingleIdentifierFieldName();

        if (isset($rootClass->associationMappings[$identifier])) {
            throw new RuntimeException('Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.');
        }

        $query = $this->_getQuery();

        $query->setHint(
            self::IDENTIFIER_TYPE,
            Type::getType($rootClass->fieldMappings[$identifier]->type),
        );

        $query->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true);

        $pathExpression = new PathExpression(
            PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
            $rootAlias,
            $identifier,
        );

        $pathExpression->type = PathExpression::TYPE_STATE_FIELD;

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. Call $paginator->setUseOutputWalkers(true) before iterating, or simply do not disable output walkers.
  2. If the underlying query sets Query::HINT_CUSTOM_OUTPUT_WALKER for its own reasons, remove that hint before paginating - its presence forces tree-walker mode.
  3. Longer term, replace the association-only primary key with a surrogate integer id plus a unique constraint on the FK column.

Example fix

// before
$paginator = new Paginator($query);
$paginator->setUseOutputWalkers(false); // RuntimeException when identifier is a FK
foreach ($paginator as $row) { /* ... */ }

// after
$paginator = new Paginator($query);
$paginator->setUseOutputWalkers(true);
foreach ($paginator as $row) { /* ... */ }
Defensive patterns

Strategy: validation

Validate before calling

// Guard: entities whose id is an association require output walkers
$class   = $em->getClassMetadata($rootEntityClass);
$idField = $class->getSingleIdentifierFieldName();
$needsOutputWalkers = isset($class->associationMappings[$idField]);

$paginator = new Paginator($query);
if ($needsOutputWalkers) {
    $paginator->setUseOutputWalkers(true);
}

Prevention

When it happens

Trigger: Iterating a Paginator with fetchJoinCollection=true (the constructor default) and maxResults set, where (a) you called $paginator->setUseOutputWalkers(false), or (b) the query itself carries Query::HINT_CUSTOM_OUTPUT_WALKER - Paginator::useOutputWalker() returns false in that case and silently switches to tree walkers. The root entity's single identifier field resolves to an entry in associationMappings (association key / @Id @ManyToOne).

Common situations: Entities modelling join tables whose PK is a FK to another entity; older blog posts recommending setUseOutputWalkers(false) as a pagination speed-up copied into modern code; a query-level custom output walker hint accidentally forcing the paginator onto the tree-walker path.

Related errors


AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21). Data as JSON: /api/errors/7be8c6c86b02a979. Report an issue: GitHub.