{"record":{"id":"c9031a802d1f65bb","repo":"getgrav/grav","slug":"unsupported-media-filter-operator-s","errorCode":null,"errorMessage":"Unsupported media filter operator \"%s\".","messagePattern":"Unsupported media filter operator \"(.+?)\"\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/Page/Medium/AbstractMedia.php","lineNumber":203,"sourceCode":"     *\n     * Returns a new collection (same type) containing only the media whose\n     * `$field` satisfies `$operator` against `$value`, so calls chain:\n     * `page.media.filterBy('rating', 3, '>=').sortBy('rating', 'desc')`.\n     *\n     * Operators: `== != > >= < <= in contains`. Comparison is\n     * loose-comparison-safe — numeric only when both operands are numeric,\n     * otherwise a strict string compare. For list fields (e.g. `tags`),\n     * `contains` tests membership and `in` tests intersection with `$value`.\n     *\n     * @param string $field    Metadata key (dot notation supported).\n     * @param mixed  $value    Value to compare against.\n     * @param string $operator One of {@see META_OPERATORS}.\n     * @return static\n     */\n    public function filterBy($field, $value, $operator = '==')\n    {\n        if (!in_array($operator, self::META_OPERATORS, true)) {\n            throw new \\InvalidArgumentException(sprintf('Unsupported media filter operator \"%s\".', $operator));\n        }\n\n        $items = array_filter(\n            $this->all(),\n            static fn($medium) => self::compareMeta($medium->get($field), $value, $operator)\n        );\n\n        return $this->createFrom($items);\n    }\n\n    /**\n     * Filter the collection by several equality criteria at once (ANDed).\n     *\n     * Each `field => value` pair must match. A scalar value is compared with\n     * `==`; an array value is treated as an `in` set (the field must equal one\n     * of the listed values). For anything beyond equality/membership, chain\n     * {@see filterBy()} calls instead.\n     *","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/Page/Medium/AbstractMedia.php#L185-L221","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\n$videos = $media->filterBy('meta.type', 'video', '=');\n\n// after\n$op = $request->get('op', '==');\n$op = in_array($op, \\Grav\\Common\\Page\\Medium\\AbstractMedia::META_OPERATORS, true) ? $op : '==';\n$videos = $media->filterBy('meta.type', 'video', $op);","handlingStrategy":"type-guard","validationCode":"use Grav\\Common\\Page\\Medium\\AbstractMedia;\n$op = $request->get('op', '==');\nif (!\\in_array($op, AbstractMedia::META_OPERATORS, true)) {\n    $op = '=='; // or reject with 422\n}\n$media->filterBy($field, $value, $op);","typeGuard":"use Grav\\Common\\Page\\Medium\\AbstractMedia;\nfunction isSupportedMetaOperator(string $op): bool\n{\n    return \\in_array($op, AbstractMedia::META_OPERATORS, true);\n}","tryCatchPattern":"try {\n    $result = $media->filterBy($field, $value, $op);\n} catch (\\InvalidArgumentException $e) {\n    // 422: unsupported operator — list the allowed set in the response\n}","preventionTips":["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."],"tags":["media","filter","operator","validation","collections"],"backgroundTag":"invalid-filter-operator","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}