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

  1. Replace add('where', $x, true) with $qb->andWhere($x) (or orWhere) so the combination is explicit; likewise andHaving/orHaving.
  2. When merging builders, special-case where/having: fetch getDQLPart('where') and combine via andWhere()/andHaving().
  3. 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

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


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