passbolt/passbolt_api · error · BadRequestException
Invalid query string. The contain parameter should be an…
Error message
Invalid query string. The contain parameter should be an array.
What it means
QueryStringComponent::normalizeQueryItems() requires the `contain` query parameter to be an array. If `contain` is present but not an array it throws BadRequestException('Invalid query string. The contain parameter should be an array.') producing a 400. Contain clauses tell the API which associations to embed, and each value is normalized to a boolean.
Solutions
- Use array syntax: `?contain[]=profile` (or `?contain%5B%5D=profile`) instead of a bare scalar value.
- For multiple associations repeat the parameter: `?contain[]=profile&contain[]=groups-users`.
- Fix the client query builder so contain keys are emitted as arrays, and verify bracket URL-encoding.
- If you don't need associations, omit the contain parameter entirely.
Example fix
// before — scalar contain, rejected GET /users.json?contain=profile // after — array syntax GET /users.json?contain[]=profile // multiple associations: GET /users.json?contain[]=profile&contain[]=gpgkey
Defensive patterns
Strategy: type-guard
Validate before calling
// ensure contain serializes as array query params
function buildContainParams(contains) {
const p = new URLSearchParams();
for (const c of contains) p.append('contain[]', c); // emits contain[]=value
return p;
} Type guard
const isContainArray = (v) => v === undefined || v === null || Array.isArray(v); // a bare string like 'profile' is rejected server-side
Try / catch
try {
return await api.get('/users.json', { params });
} catch (e) {
if (e.response?.status === 400 && /contain parameter should be an array/.test(e.response?.data?.message ?? '')) {
throw new Error('Send associations as contain[]=name, not contain=name');
}
throw e;
} Prevention
- Always emit `contain[]=` (URL-encoded `contain%5B%5D=`) even for a single association.
- Restrict contain names to associations documented for the endpoint to avoid follow-up 400s.
- Share one query-serialization helper for filter/contain so both keep bracket array syntax.
- Test query builders with encoded brackets to catch encoders that strip or escape `[]` incorrectly.
When it happens
Trigger: GET collection/detail endpoints with `?contain=<scalar>` — e.g. `?contain=profile` instead of `?contain[]=profile`, or `?contain=1`; a contain value that URL parsing turns into a string rather than an array element.
Common situations: Clients omitting the `[]` in `contain[]=`; copying single-association examples written as `contain=groups` from other APIs; URL-encoding bugs dropping brackets; templated API wrappers joining contain values with commas into one string.
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 query string. The filter parameter should be an…
- " " is not a valid contain value.
- Invalid filter.
- Invalid order.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/a9ade49cad3fb9ac.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controller/Component/QueryStringComponent.php:146
if ($parentId === 'false' || $parentId === '0') {
$query['filter']['has-parent'][$i] = false;
}
}
} elseif ($filterName === 'from') {
try {
$query['filter']['from'] = new DateTime($query['filter']['from']);
} catch (Exception $e) {
$query['filter']['from'] = false;
}
} elseif ($filterName === 'frequency') {
$query['filter'][$filterName] = self::normalizeInteger($filter);
}
}
}
// idem with contain clauses
if (isset($query['contain'])) {
if (!is_array($query['contain'])) {
throw new BadRequestException(__('Invalid query string. The contain parameter should be an array.'));
}
foreach ($query['contain'] as $containName => $contain) {
$query['contain'][$containName] = self::normalizeBoolean($contain);
}
}
return $query;
}
/**
* Extract array string items
*
* @param array $query original query string items
* @param array $allowedQueryItems whitelist
* @return array $query the sanitized query
*/
public static function unsetUnwantedQueryItems(array $query, array $allowedQueryItems): array
{View on GitHub (pinned to 31c1bbc10f)