passbolt/passbolt_api · error · BadRequestException
Invalid order.
Error message
Invalid order. {inner message} What it means
Thrown by QueryStringComponent::validateQueryItems when validating the 'order' query string key. validateOrders() checks each sort field against the controller's $allowedQueryItems whitelist; on failure the CakeException is re-thrown as a BadRequestException prefixed with 'Invalid order.' plus the inner reason. It means the client requested sorting on a field that is not allowed for this endpoint.
Solutions
- Read the inner message after 'Invalid order.' to see which field/direction was rejected.
- Use only fields listed in the endpoint's allowed query items (see the controller's QueryStringComponent config).
- Use ASC or DESC as the sort direction only.
- Drop the order clause to get the API default sort.
- If the field should be sortable, add it to $allowedQueryItems for that controller action.
Example fix
// before GET /users.json?order[User.profil]=ASC // after GET /users.json?order[User.profile]=ASC
Defensive patterns
Strategy: validation
Validate before calling
$allowedOrderFields = ['User.username','User.created','User.modified','Profile.first_name','Resource.name','Resource.created','Resource.modified']; // per endpoint
foreach ((array)($query['order'] ?? []) as $field => $direction) {
$dir = strtoupper((string)$direction);
if (!in_array($dir, ['ASC','DESC'], true)) {
throw new InvalidArgumentException("Invalid order direction: $direction");
}
if (!in_array($field, $allowedOrderFields, true)) {
throw new InvalidArgumentException("Field not sortable: $field");
}
} Type guard
function isValidOrderClause(mixed $order, array $allowed): bool {
if (!is_array($order) || $order === []) return false;
foreach ($order as $field => $dir) {
if (!in_array($field, $allowed, true)) return false;
if (!in_array(strtoupper((string)$dir), ['ASC','DESC'], true)) return false;
}
return true;
} Try / catch
try {
$result = $api->get('/users.json', ['query' => ['order' => $order]]);
} catch (BadRequestException $e) {
if (str_starts_with($e->getMessage(), 'Invalid order.')) {
$result = $api->get('/users.json'); // fall back to default sort
} else {
throw $e;
}
} Prevention
- Only sort on fields returned in the endpoint response and documented as sortable
- Always use ASC/DESC (uppercase) as direction
- Fetch the endpoint's allowed query items from docs or the controller before adding sort columns
- Update client sort mappings when upgrading passbolt versions
- Unit-test generated order clauses against the API in CI
When it happens
Trigger: GET requests with ?order[...] naming a field absent from the controller's allowed query items, or a direction other than ASC/DESC, e.g. ?order[Resource.username]=ASCENDING or sorting a non-whitelisted property.
Common situations: Clients sorting on fields removed/renamed in a passbolt version; hand-built URLs guessing field names; front-end table sort columns not matching the API's allowed order fields; case mismatches in field names.
Understand the failure class
Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.
Related errors
- Invalid contain.
- Invalid filter.
- " " is not a valid order.
- " " is not a valid contain value.
- " " is not a valid datetime for filter .
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/91ad09b6fac6bb04.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controller/Component/QueryStringComponent.php:251
* @return bool true if validate
* @throws \Cake\Http\Exception\BadRequestException if a validation error occurs
*/
public static function validateQueryItems(array $query, array $allowedQueryItems, array $filterValidators): bool
{
foreach ($query as $key => $parameters) {
switch ($key) {
case 'filter':
try {
self::validateFilters($parameters, $filterValidators);
} catch (CakeException $e) {
throw new BadRequestException(__('Invalid filter.') . ' ' . $e->getMessage());
}
break;
case 'order':
try {
self::validateOrders($parameters, $allowedQueryItems);
} catch (CakeException $e) {
throw new BadRequestException(__('Invalid order.') . ' ' . $e->getMessage());
}
break;
case 'contain':
try {
self::validateContain($parameters);
} catch (CakeException $e) {
throw new BadRequestException(__('Invalid contain.') . ' ' . $e->getMessage());
}
break;
}
}
return true;
}
/**
* Validate filters
*View on GitHub (pinned to 31c1bbc10f)