passbolt/passbolt_api · error · CakeException
No validation rule for filter
Error message
No validation rule for filter {0}. Please create one. What it means
Raised inside QueryStringComponent::validateFilters() (surfaced to clients as 'Invalid filter. No validation rule for filter {name}.') when a filter name is not one of the hard-coded cases AND no custom validator callable was registered for it in $filterValidators. The component intentionally fails closed: unknown filters must be explicitly whitelisted with a validation rule before they can be used.
Solutions
- Check the filter name for typos against the built-in list in validateFilters().
- If it is a legitimate new filter, register a validator: pass ['your-filter' => fn($values) => (bool)$values] in the QueryStringComponent config ($filterValidators) of the controller.
- If it comes from a plugin, update the plugin or the passbolt version so the filter's validator exists.
- If the filter is not needed, remove it from the request.
- Confirm client and server API versions match so the client only sends supported filters.
Example fix
// before (controller)
$this->loadComponent('QueryString', ['filterValidators' => []]);
// after
$this->loadComponent('QueryString', ['filterValidators' => [
'is-expiring' => function ($values) { return is_scalar($values); },
]]); Defensive patterns
Strategy: validation
Validate before calling
// Server-side guard before relying on a custom filter
$builtin = ['search','from','created-before','created-after','has-access','has-id','has-managers','has-users','has-groups','has-parent','is-shared-with-group','modified-after','is-active','is-admin','is-favorite','is-owned-by-me','is-shared-with-me','is-shared','is-success','is-deleted','expired','deleted','has-tag','frequency','metadata_key_type'];
$registered = array_keys($filterValidators); // passed to QueryStringComponent
if (!in_array($myFilterName, $builtin, true) && !in_array($myFilterName, $registered, true)) {
throw new LogicException("Filter '$myFilterName' has no validator; register it in filterValidators");
} Type guard
function filterHasValidator(string $name, array $filterValidators, array $builtin): bool {
return in_array($name, $builtin, true) || array_key_exists($name, $filterValidators);
} Try / catch
try {
$result = $api->get('/items.json', ['query' => ['filter' => $filters]]);
} catch (BadRequestException $e) {
if (str_contains($e->getMessage(), 'No validation rule for filter')) {
$logger->error('Server does not support filter', ['filter' => array_keys($filters)]);
throw new UnsupportedFilterException($e->getMessage(), previous: $e);
}
throw $e;
} Prevention
- When adding a new filter to a plugin, register its validator in the same change
- Client-side: only send filters listed in the API version you target
- Add a smoke test hitting the endpoint with every filter your client emits
- Check the built-in switch cases in validateFilters() before inventing a custom name
- Fail fast in dev: log full BadRequest messages so 'No validation rule' surfaces immediately
When it happens
Trigger: A request uses ?filter[some-custom-key]=... where some-custom-key is neither in the switch (search, from, created-*, has-*, is-*, modified-after, expired, deleted, has-tag, frequency, metadata_key_type) nor present in the $filterValidators array passed by the controller/plugin.
Common situations: A plugin introduced a new filter but forgot to register its validator; a typo in a filter name so it falls through to the default branch; a client sending filters from a different (newer/older) API version; developers testing a new filter endpoint before writing the validator.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Filter is not valid.
- Invalid filter.
- " " is not a valid contain value.
- " " is not a valid datetime for filter .
- " " is not a valid group filter.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/3cb7538fc5c21d7e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controller/Component/QueryStringComponent.php:349
case 'is-deleted':
case 'expired':
case 'deleted':
self::validateFilterBoolean($values, $filterName);
break;
case 'has-tag':
self::validateFilterString($values, $filterName);
break;
case 'frequency':
self::validateFilterInteger($values, $filterName);
break;
case 'metadata_key_type':
self::validateFilterInList($values, $filterName, ['user_key', 'shared_key']);
break;
default:
// Check if custom filter validators were defined for this filter
if (!isset($filterValidators[$filterName])) {
$msg = __('No validation rule for filter {0}. Please create one.', $filterName);
throw new CakeException($msg);
}
if (!call_user_func($filterValidators[$filterName], $values)) {
throw new CakeException(__('Filter {0} is not valid.', $filterName));
}
break;
}
}
}
return true;
}
/**
* Check if the filter is a valid string
*
* @param mixed $value to checkView on GitHub (pinned to 31c1bbc10f)