doctrine/orm · error · RuntimeException
Unknown composite {type}
Error message
Unknown composite {type} What it means
When a Criteria containing a CompositeExpression filters a persistent collection (matching()) or is walked into SQL, SqlExpressionVisitor::walkCompositeExpression() renders the expression by matching on the doctrine/collections type constants TYPE_AND, TYPE_OR and TYPE_NOT. Any other type string hits the default arm and throws RuntimeException. Modern doctrine/collections validates types at construction, so this means a hand-built/subclassed CompositeExpression or a mismatched collections version.
Source
Thrown at src/Persisters/SqlExpressionVisitor.php:68
/**
* Converts a composite expression into the target query language output.
*
* @throws RuntimeException
*/
public function walkCompositeExpression(CompositeExpression $expr): string
{
$expressionList = [];
foreach ($expr->getExpressionList() as $child) {
$expressionList[] = $this->dispatch($child);
}
return match ($expr->getType()) {
CompositeExpression::TYPE_AND => '(' . implode(' AND ', $expressionList) . ')',
CompositeExpression::TYPE_OR => '(' . implode(' OR ', $expressionList) . ')',
CompositeExpression::TYPE_NOT => 'NOT (' . $expressionList[0] . ')',
default => throw new RuntimeException('Unknown composite ' . $expr->getType()),
};
}
/**
* Converts a value expression into the target query language part.
*/
public function walkValue(Value $value): string
{
return '?';
}
}
View on GitHub (pinned to d9b9ff7301)
Solutions
- Use the factories: Criteria::expr()->andX(...), ->orX(...), ->notX(...) — they only produce valid types
- Update doctrine/collections to a current version that validates types in the constructor
- Never construct CompositeExpression with literal type strings
Example fix
// before
$criteria->andWhere(new CompositeExpression('XOR', [$expr1, $expr2]));
// after
$criteria->andWhere(Criteria::expr()->orX(
Criteria::expr()->andX($expr1, $expr2),
Criteria::expr()->andX($expr3)
)); Defensive patterns
Strategy: type-guard
Validate before calling
if (! in_array($expr->getType(), [CompositeExpression::TYPE_AND, CompositeExpression::TYPE_OR, CompositeExpression::TYPE_NOT], true)) { throw new InvalidArgumentException('Bad composite type'); } Type guard
/** @param mixed $expr */
function isSupportedComposite(mixed $expr): bool
{
return $expr instanceof CompositeExpression
&& in_array($expr->getType(), [CompositeExpression::TYPE_AND, CompositeExpression::TYPE_OR, CompositeExpression::TYPE_NOT], true);
} Prevention
- Build composites only via Criteria::expr() factories
- Keep doctrine/collections current
- Never pass raw type strings to CompositeExpression
When it happens
Trigger: new CompositeExpression('XOR', [...]) or a subclass injecting a custom type, then Criteria->andWhere()/orWhere() passed to PersistentCollection::matching()/EntityRepository::matching(); using an outdated doctrine/collections that permits arbitrary type strings.
Common situations: Custom expression classes extending doctrine/collections' CompositeExpression; pinning an old doctrine/collections version alongside newer doctrine/orm.
Related errors
- Filtering a collection by Criteria is not supported by this
- Could not resolve type of column "%s" of class "%s"
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/28a5cd12c2d5252f.
Report an issue: GitHub.