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

  1. Read the inner message after 'Invalid order.' to see which field/direction was rejected.
  2. Use only fields listed in the endpoint's allowed query items (see the controller's QueryStringComponent config).
  3. Use ASC or DESC as the sort direction only.
  4. Drop the order clause to get the API default sort.
  5. 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

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


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)