getgrav/grav · error · InvalidArgumentException
Unsupported media filter operator "%s".
Error message
Unsupported media filter operator "%s".
What it means
AbstractMedia::filterBy() (line 203) validates $operator against the class constant META_OPERATORS = ['==','!=','>','>=','<','<=','in','contains'] using a strict in_array check, and throws InvalidArgumentException for anything else. Even near-misses like '=', 'IN', or an operator with stray whitespace are rejected. Per the docblock, 'contains' tests list membership and 'in' tests intersection for list fields.
Source
Thrown at system/src/Grav/Common/Page/Medium/AbstractMedia.php:203
*
* Returns a new collection (same type) containing only the media whose
* `$field` satisfies `$operator` against `$value`, so calls chain:
* `page.media.filterBy('rating', 3, '>=').sortBy('rating', 'desc')`.
*
* Operators: `== != > >= < <= in contains`. Comparison is
* loose-comparison-safe — numeric only when both operands are numeric,
* otherwise a strict string compare. For list fields (e.g. `tags`),
* `contains` tests membership and `in` tests intersection with `$value`.
*
* @param string $field Metadata key (dot notation supported).
* @param mixed $value Value to compare against.
* @param string $operator One of {@see META_OPERATORS}.
* @return static
*/
public function filterBy($field, $value, $operator = '==')
{
if (!in_array($operator, self::META_OPERATORS, true)) {
throw new \InvalidArgumentException(sprintf('Unsupported media filter operator "%s".', $operator));
}
$items = array_filter(
$this->all(),
static fn($medium) => self::compareMeta($medium->get($field), $value, $operator)
);
return $this->createFrom($items);
}
/**
* Filter the collection by several equality criteria at once (ANDed).
*
* Each `field => value` pair must match. A scalar value is compared with
* `==`; an array value is treated as an `in` set (the field must equal one
* of the listed values). For anything beyond equality/membership, chain
* {@see filterBy()} calls instead.
*View on GitHub (pinned to 6040efed04)
Solutions
- Use one of the eight supported operators exactly as written: == != > >= < <= in contains.
- Whitelist incoming operators against AbstractMedia::META_OPERATORS and normalize common aliases (= → ==) before calling filterBy().
- For substring matching use 'contains'; for list membership use 'in' — there is no 'like' operator.
Example fix
// before
$videos = $media->filterBy('meta.type', 'video', '=');
// after
$op = $request->get('op', '==');
$op = in_array($op, \Grav\Common\Page\Medium\AbstractMedia::META_OPERATORS, true) ? $op : '==';
$videos = $media->filterBy('meta.type', 'video', $op); Defensive patterns
Strategy: type-guard
Validate before calling
use Grav\Common\Page\Medium\AbstractMedia;
$op = $request->get('op', '==');
if (!\in_array($op, AbstractMedia::META_OPERATORS, true)) {
$op = '=='; // or reject with 422
}
$media->filterBy($field, $value, $op); Type guard
use Grav\Common\Page\Medium\AbstractMedia;
function isSupportedMetaOperator(string $op): bool
{
return \in_array($op, AbstractMedia::META_OPERATORS, true);
} Try / catch
try {
$result = $media->filterBy($field, $value, $op);
} catch (\InvalidArgumentException $e) {
// 422: unsupported operator — list the allowed set in the response
} Prevention
- Map user-facing operators (=, like) to the supported set at the API boundary.
- Centralize the alias map (e.g. '=' => '==') instead of scattering normalizations.
- Use 'contains' for substring match and 'in' for list membership.
When it happens
Trigger: Calling $media->filterBy($field, $value, '=') (single equals), 'like', '=~', 'not in', or a wrongly-cased/spaced operator; forwarding a user-supplied operator query parameter straight from a URL into filterBy().
Common situations: Porting SQL or Twig query syntax to media filtering; API endpoints exposing the operator to clients; confusing this API with page collection filtering that accepts a different operator vocabulary.
Related errors
- Theme name not provided.
- The callable passed to the "array_group_by" filter must be a
- Twig |filter("{arrow}") is not allowed.
- Twig |map("{arrow}") is not allowed.
- Twig |reduce("{arrow}") is not allowed.
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/c9031a802d1f65bb.
Report an issue: GitHub.