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

  1. Use one of the eight supported operators exactly as written: == != > >= < <= in contains.
  2. Whitelist incoming operators against AbstractMedia::META_OPERATORS and normalize common aliases (= → ==) before calling filterBy().
  3. 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

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


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/c9031a802d1f65bb. Report an issue: GitHub.