doctrine/orm · error · InvalidArgumentException

Expression of type '%s' not allowed in this context.

Error message

Expression of type '%s' not allowed in this context.

What it means

Expr\Base::add() underpins the expression classes used by QueryBuilder (Andx, Orx, Select, OrderBy, GroupBy). An argument must be a string or an instance of one of the subclass's allowedClasses — Andx/Orx accept Comparison, Func and each other, Select accepts only Func, OrderBy/GroupBy accept only strings. Any other value (ints, bools, arbitrary objects, even Stringable objects) throws InvalidArgumentException.

Source

Thrown at src/Query/Expr/Base.php:74

        }

        return $this;
    }

    /**
     * @param string|Stringable|null $arg
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     */
    public function add(mixed $arg): static
    {
        if ($arg !== null && (! $arg instanceof self || $arg->count() > 0)) {
            // If we decide to keep Expr\Base instances, we can use this check
            // @phpstan-ignore function.alreadyNarrowedType (input validation)
            if (! is_string($arg) && ! (is_object($arg) && in_array($arg::class, $this->allowedClasses, true))) {
                throw new InvalidArgumentException(sprintf(
                    "Expression of type '%s' not allowed in this context.",
                    get_debug_type($arg),
                ));
            }

            $this->parts[] = $arg;
        }

        return $this;
    }

    /** @phpstan-return 0|positive-int */
    public function count(): int
    {
        return count($this->parts);
    }

    public function __toString(): string

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. Cast scalars/objects to string: ->add((string) $value) for raw DQL fragments
  2. Build conditions with the factory: $qb->expr()->eq('u.id', ':id') produces a Comparison accepted by andX()
  3. Match the expression class to its allowed types — only strings for OrderBy/GroupBy, Func for Select
  4. Filter input arrays before addMultiple(): array_filter($parts, is_string(...) || instanceof allowed)

Example fix

// before
$orx = $qb->expr()->orX();
$orx->add($maybeInt); // throws for non-string
// after
$orx = $qb->expr()->orX();
$orx->add((string) $maybeInt);
// or, for comparisons:
$orx->add($qb->expr()->eq('u.status', ':status'));
Defensive patterns

Strategy: type-guard

Validate before calling

$allowed = [Comparison::class, Func::class, Orx::class, Andx::class];
foreach ($parts as $p) {
    if (! is_string($p) && ! (is_object($p) && in_array($p::class, $allowed, true))) { continue; }
    $expr->add($p);
}

Type guard

/** @param mixed $part */
function isAllowedExprPart(mixed $part, array $allowedClasses): bool
{
    return is_string($part) || (is_object($part) && in_array($part::class, $allowedClasses, true));
}

Try / catch

try { $andX->add($dynamicPart); } catch (InvalidArgumentException) { $andX->add((string) $dynamicPart); }

Prevention

When it happens

Trigger: $expr->andX()->add(new \DateTimeImmutable()) or ->add(123); passing an Expr\OrderBy or a plain stringable value object into Andx/Orx; passing a Comparison into OrderBy::add(); feeding expr parts from untrusted/mixed-typed arrays.

Common situations: Dynamically assembled filters where a null/int sneaks into addMultiple(); assuming __toString objects are accepted (they are not); mixing QueryBuilder expression objects into the wrong expression type.

Related errors


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