symfony/http-kernel · error · HttpException
Invalid query parameter
Error message
Invalid query parameter "%s".
What it means
Thrown when a #[MapQueryParameter] argument typed as array (non-variadic, no custom filter) receives query values that are not all arrays. The resolver filters the value keeping only elements that are themselves arrays (array_filter with is_array); if any element was dropped and FILTER_NULL_ON_FAILURE was not set, the whole parameter is rejected. In practice this fires when a plain scalar or list of scalars is sent for a parameter declared as array-of-array.
Solutions
- Send nested query syntax producing arrays of arrays, e.g. ?filter[from]=1&filter[to]=2 becomes filter as an array of arrays only if each value is itself an array — or restructure the URL
- If a flat array is intended, make the argument variadic (#[MapQueryParameter] array ...$ids) or add an explicit filter, e.g. #[MapQueryParameter(filter: FILTER_DEFAULT)] which routes through filter_var instead of this array branch
- Pass flags: FILTER_NULL_ON_FAILURE in #[MapQueryParameter] to get null/dropped values instead of a 4xx
- Change the controller type to string/int and split manually if only one value is expected
Example fix
// before
public function search(#[MapQueryParameter] array $tags) { ... }
// request: GET /search?tags=php -> 422 Invalid query parameter "tags"
// after
public function search(#[MapQueryParameter(filter: \FILTER_DEFAULT)] array $tags) { ... }
// request: GET /search?tags[]=php&tags[]=symfony works Defensive patterns
Strategy: validation
Validate before calling
// send nested arrays only if the argument is plain `array`, else send flat lists with a filter configured
const usp = new URLSearchParams();
['php','symfony'].forEach(t => usp.append('tags[]', t)); // flat list; pair with filter: FILTER_DEFAULT on the server Type guard
function isArrayOfArrays(v: unknown): v is unknown[][] {
return Array.isArray(v) && v.every(Array.isArray);
} Try / catch
try {
const res = await fetch(url);
if (res.status === 422 && (await res.text()).includes('Invalid query parameter')) {
// reshape the query value (scalar -> nested array or flat list) and retry
}
} catch (e) {} Prevention
- Match client query syntax to the controller's declared type before shipping
- Prefer variadic arguments or an explicit filter for flat list parameters
- Document each endpoint's expected query-string shape
- Add contract tests covering array-shaped query params
When it happens
Trigger: Controller declares #[MapQueryParameter] array $filter and the client sends ?filter=foo (scalar) or ?filter[]=1&filter[]=2 (array of scalars); only nested-array values like filter[a][b]=1 survive the is_array filter, so anything else triggers the throw unless FILTER_NULL_ON_FAILURE is passed in the attribute flags.
Common situations: Client sends a simple list (?ids[]=1&ids[]=2) while the controller argument is typed array with no variadic and no filter — the developer expected a flat array but Symfony's unfiltered array branch demands arrays-of-arrays; version migrations where behavior around typed array query params changed.
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
- Missing query parameter
- The validation groups expression or closure must return a…
- Nested expressions in validation groups are not supported…
- Nested closures in validation groups are not supported. Use…
- GroupSequence cannot be used inside an array of validation…
AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13).
Data as JSON: /api/errors/ebe16fdcef485c29.
Report an issue: GitHub.
Appendix: source
Thrown at Controller/ArgumentResolver/QueryParameterValueResolver.php:64
if ($argument->isNullable() || $argument->hasDefaultValue()) {
return [];
}
throw HttpException::fromStatusCode($validationFailedCode, \sprintf('Missing query parameter "%s".', $name));
}
$value = $request->query->all()[$name];
$type = $argument->getType();
if (null === $attribute->filter && 'array' === $type) {
if (!$argument->isVariadic()) {
return [(array) $value];
}
$filtered = array_values(array_filter((array) $value, \is_array(...)));
if ($filtered !== $value && !($attribute->flags & \FILTER_NULL_ON_FAILURE)) {
throw HttpException::fromStatusCode($validationFailedCode, \sprintf('Invalid query parameter "%s".', $name));
}
return $filtered;
}
$options = [
'flags' => $attribute->flags | \FILTER_NULL_ON_FAILURE,
'options' => $attribute->options,
];
if ('array' === $type || $argument->isVariadic()) {
$value = (array) $value;
$options['flags'] |= \FILTER_REQUIRE_ARRAY;
} else {
$options['flags'] |= \FILTER_REQUIRE_SCALAR;
}
$uidType = null;View on GitHub (pinned to aa3a39d728)