{"record":{"id":"c703b95dd329823a","repo":"doctrine/orm","slug":"setting-a-limit-is-not-supported-for-delete-or-upd","errorCode":null,"errorMessage":"Setting a limit is not supported for delete or update queries.","messagePattern":"Setting a limit is not supported for delete or update queries\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/QueryBuilder.php","lineNumber":595,"sourceCode":"    }\n\n    /**\n     * Gets the position of the first result the query object was set to retrieve (the \"offset\").\n     */\n    public function getFirstResult(): int\n    {\n        return $this->firstResult;\n    }\n\n    /**\n     * Sets the maximum number of results to retrieve (the \"limit\").\n     *\n     * @return $this\n     */\n    public function setMaxResults(int|null $maxResults): static\n    {\n        if ($this->type === QueryType::Delete || $this->type === QueryType::Update) {\n            throw new RuntimeException('Setting a limit is not supported for delete or update queries.');\n        }\n\n        $this->maxResults = $maxResults;\n\n        return $this;\n    }\n\n    /**\n     * Gets the maximum number of results the query object was set to retrieve (the \"limit\").\n     * Returns NULL if {@link setMaxResults} was not applied to this query builder.\n     */\n    public function getMaxResults(): int|null\n    {\n        return $this->maxResults;\n    }\n\n    /**\n     * Either appends to or replaces a single, generic query part.","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/QueryBuilder.php#L577-L613","documentation":"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.","triggerScenarios":"$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.","commonSituations":"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.","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)."],"exampleFix":"// before\n$em->createQueryBuilder()->update(User::class, 'u')->set('u.notified', ':n')\n    ->where('u.active = true')->setMaxResults(100) // RuntimeException\n    ->getQuery()->execute();\n\n// after - bound rows with a limited SELECT, then bulk update by id\n$ids = $em->createQueryBuilder()->select('u.id')->from(User::class, 'u')\n    ->where('u.active = true')->setMaxResults(100)\n    ->getQuery()->getSingleColumnResult();\n$em->createQueryBuilder()->update(User::class, 'u')->set('u.notified', ':n')\n    ->where('u.id IN (:ids)')->setParameter('ids', $ids)->setParameter('n', true)\n    ->getQuery()->execute();","handlingStrategy":"try-catch","validationCode":"// QueryBuilder::getType() is protected, so validate on the produced DQL:\n$dql    = strtoupper(ltrim((string) $qb->getDQL()));\n$isBulk = str_starts_with($dql, 'DELETE') || str_starts_with($dql, 'UPDATE');\nif ($limit !== null && ! $isBulk) {\n    $qb->setMaxResults($limit);\n}","typeGuard":null,"tryCatchPattern":"In generic decorators that may receive bulk builders, make the limit optional and degrade on purpose:\ntry {\n    $qb->setMaxResults($limit);\n} catch (\\Doctrine\\ORM\\RuntimeException $e) {\n    // bulk DELETE/UPDATE cannot be limited: skip the limit deliberately\n}","preventionTips":["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."],"tags":["doctrine-orm","query-builder","bulk-operations","limit","pagination"],"backgroundTag":"unsupported-limit-on-bulk-statement","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}