doctrine/orm · error · InvalidArgumentException
Using $append = true does not have an effect with 'where' or
Error message
Using $append = true does not have an effect with 'where' or 'having' parts. See QueryBuilder#andWhere() for an example for correct usage.
What it means
add($dqlPartName, $dqlPart, $append) is QueryBuilder's low-level part setter. For 'where' and 'having', blindly appending another part would stack predicates in a way that matches no boolean semantics users actually want, so the builder rejects $append = true for exactly those two part names and points you to andWhere()/orWhere()/andHaving()/orHaving(), which build the combined expression explicitly.
Source
Thrown at src/QueryBuilder.php:625
public function getMaxResults(): int|null
{
return $this->maxResults;
}
/**
* Either appends to or replaces a single, generic query part.
*
* The available parts are: 'select', 'from', 'join', 'set', 'where',
* 'groupBy', 'having' and 'orderBy'.
*
* @phpstan-param string|object|list<string>|array{join: array<int|string, object>} $dqlPart
*
* @return $this
*/
public function add(string $dqlPartName, string|object|array $dqlPart, bool $append = false): static
{
if ($append && ($dqlPartName === 'where' || $dqlPartName === 'having')) {
throw new InvalidArgumentException(
"Using \$append = true does not have an effect with 'where' or 'having' " .
'parts. See QueryBuilder#andWhere() for an example for correct usage.',
);
}
$isMultiple = is_array($this->dqlParts[$dqlPartName])
&& ! ($dqlPartName === 'join' && ! $append);
// Allow adding any part retrieved from self::getDQLParts().
if (is_array($dqlPart) && $dqlPartName !== 'join') {
$dqlPart = reset($dqlPart);
}
if ($dqlPartName === 'join') {
$newDqlPart = [];
foreach ($dqlPart as $k => $v) {
if (is_numeric($k)) {View on GitHub (pinned to d9b9ff7301)
Solutions
- Replace add('where', $x, true) with $qb->andWhere($x) (or orWhere) so the combination is explicit; likewise andHaving/orHaving.
- When merging builders, special-case where/having: fetch getDQLPart('where') and combine via andWhere()/andHaving().
- Use add() without the append flag only where you truly mean replacement.
Example fix
// before
$qbTarget->add('where', $qbSource->getDQLPart('where'), true); // InvalidArgumentException
// after
$where = $qbSource->getDQLPart('where');
if ($where !== null) {
$qbTarget->andWhere($where);
} Defensive patterns
Strategy: validation
Validate before calling
if ($append && in_array($part, ['where', 'having'], true)) {
// combine explicitly instead of add(..., true)
$part === 'where' ? $qb->andWhere($value) : $qb->andHaving($value);
} else {
$qb->add($part, $value, $append);
} Try / catch
In generic part copiers, catch \InvalidArgumentException and fail loudly with the part name: catch (\InvalidArgumentException $e) { throw new LogicException("Cannot append part '$part'", 0, $e); } Prevention
- Prefer typed part methods (andWhere, andHaving, addOrderBy) over add().
- Never loop blindly over getDQLParts() with append=true.
- Know which parts are single-valued (where, having) versus lists (join, groupBy, orderBy).
When it happens
Trigger: $qb->add('where', $expr, true); $qb->add('having', $havingExpr, true); builder-merge utilities that loop over getDQLParts() re-adding every part with $append = true and hit the 'where'/'having' keys.
Common situations: Utilities that clone or merge QueryBuilders by copying parts wholesale; porting old code that treated 'where' as a list; generic filter pipelines built on add().
Related errors
- No alias was set before invoking add().
- No alias was set before invoking getRootAlias().
- Setting a limit is not supported for delete or update querie
- %s(): The alias for entity %s must not be omitted.
- Uninitialized result set mapping.
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/99098fec5199a873.
Report an issue: GitHub.