doctrine/orm · error · RuntimeException
Setting a limit is not supported for delete or update querie
Error message
Setting a limit is not supported for delete or update queries.
What it means
setMaxResults() applies a result limit, which only makes sense for SELECT statements. Bulk DQL DELETE/UPDATE cannot be row-limited portably across database platforms, so as soon as the builder's type is Delete or Update, setMaxResults() throws RuntimeException instead of accepting the value.
Source
Thrown at src/QueryBuilder.php:595
}
/**
* Gets the position of the first result the query object was set to retrieve (the "offset").
*/
public function getFirstResult(): int
{
return $this->firstResult;
}
/**
* Sets the maximum number of results to retrieve (the "limit").
*
* @return $this
*/
public function setMaxResults(int|null $maxResults): static
{
if ($this->type === QueryType::Delete || $this->type === QueryType::Update) {
throw new RuntimeException('Setting a limit is not supported for delete or update queries.');
}
$this->maxResults = $maxResults;
return $this;
}
/**
* Gets the maximum number of results the query object was set to retrieve (the "limit").
* Returns NULL if {@link setMaxResults} was not applied to this query builder.
*/
public function getMaxResults(): int|null
{
return $this->maxResults;
}
/**
* Either appends to or replaces a single, generic query part.View on GitHub (pinned to d9b9ff7301)
Solutions
- Remove setMaxResults() from delete/update paths; DQL bulk statements affect all rows matching the WHERE.
- To bound affected rows, first SELECT the ids with setMaxResults(), then run the bulk statement WHERE id IN (:ids).
- In shared helpers, apply the limit only when you know the builder is a SELECT (QueryBuilder::getType() is protected, so track the statement kind you built).
Example fix
// before
$em->createQueryBuilder()->update(User::class, 'u')->set('u.notified', ':n')
->where('u.active = true')->setMaxResults(100) // RuntimeException
->getQuery()->execute();
// after - bound rows with a limited SELECT, then bulk update by id
$ids = $em->createQueryBuilder()->select('u.id')->from(User::class, 'u')
->where('u.active = true')->setMaxResults(100)
->getQuery()->getSingleColumnResult();
$em->createQueryBuilder()->update(User::class, 'u')->set('u.notified', ':n')
->where('u.id IN (:ids)')->setParameter('ids', $ids)->setParameter('n', true)
->getQuery()->execute(); Defensive patterns
Strategy: try-catch
Validate before calling
// QueryBuilder::getType() is protected, so validate on the produced DQL:
$dql = strtoupper(ltrim((string) $qb->getDQL()));
$isBulk = str_starts_with($dql, 'DELETE') || str_starts_with($dql, 'UPDATE');
if ($limit !== null && ! $isBulk) {
$qb->setMaxResults($limit);
} Try / catch
In generic decorators that may receive bulk builders, make the limit optional and degrade on purpose:
try {
$qb->setMaxResults($limit);
} catch (\Doctrine\ORM\RuntimeException $e) {
// bulk DELETE/UPDATE cannot be limited: skip the limit deliberately
} Prevention
- Keep pagination code out of bulk update/delete code paths.
- Cap bulk operations by id: SELECT ids with LIMIT, then WHERE id IN (...).
- Remember MySQL's DELETE/UPDATE ... LIMIT is vendor SQL, not DQL.
When it happens
Trigger: $qb->delete(User::class, 'u')->where(...)->setMaxResults(100); update() followed by setMaxResults(); generic list helpers or pagination decorators that unconditionally apply a limit to whatever QueryBuilder they receive, including bulk ones.
Common situations: Porting MySQL-specific DELETE ... LIMIT / UPDATE ... LIMIT SQL to DQL; shared repository helpers that always apply pagination defaults; cleanup jobs trying to cap affected rows per run.
Related errors
- %s(): The alias for entity %s must not be omitted.
- No alias was set before invoking getRootAlias().
- Using $append = true does not have an effect with 'where' or
- No alias was set before invoking add().
- Not all identifier properties can be found in the ResultSetM
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/c703b95dd329823a.
Report an issue: GitHub.