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
- Call $paginator->setUseOutputWalkers(true) before iterating, or simply do not disable output walkers.
- 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.
- 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
- Never call setUseOutputWalkers(false) for entities with @Id @ManyToOne identifiers
- Remember a query-level HINT_CUSTOM_OUTPUT_WALKER silently forces the Paginator onto tree walkers
- Prefer surrogate integer primary keys over FK-only identifiers
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
- Cannot select distinct identifiers from query with LIMIT and
- Not all identifier properties can be found in the ResultSetM
- Can only process queries that select only one FROM component
- Cannot count query which selects two FROM components, cannot
- Setting a limit is not supported for delete or update querie
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/7be8c6c86b02a979.
Report an issue: GitHub.