symfony/http-kernel · error · HttpException
Missing query parameter
Error message
Missing query parameter "%s".
What it means
This HttpException is thrown by QueryParameterValueResolver::resolve() when a controller argument annotated with #[MapQueryParameter] is not present in the request's query string, and the argument is neither nullable nor has a default value. The library throws it to fail the request early with a 4xx status (configurable via validationFailedStatusCode, 422 by default) instead of passing null into the controller. It signals a client-side problem: the caller omitted a required query parameter.
Solutions
- Append the missing parameter to the request URL, e.g. /api/items?page=1
- Verify the query parameter name matches the controller argument name (or pass the explicit name: #[MapQueryParameter(name: 'per_page')])
- If the parameter is truly optional, make the argument nullable or give it a default: function list(#[MapQueryParameter] ?int $page = null)
- Confirm the client sends it in the query string, not the request body
- Set validationFailedStatusCode on the attribute if a different HTTP status (e.g. 400) is desired
Example fix
// before
#[Route('/items')]
public function list(#[MapQueryParameter] int $page) { ... }
// request: GET /items -> 422 Missing query parameter "page"
// after
#[Route('/items')]
public function list(#[MapQueryParameter] ?int $page = null) { ... }
// or client calls GET /items?page=1 Defensive patterns
Strategy: validation
Validate before calling
const params = new URLSearchParams({ page: '1' });
if (!params.has('page')) throw new Error('page query parameter is required');
fetch(`/items?${params}`); Type guard
function hasQueryParam(url: URL, name: string): boolean {
return url.searchParams.has(name);
} Try / catch
try {
const res = await fetch('/items');
if (res.status === 422) {
const body = await res.text();
if (body.includes('Missing query parameter')) { /* add param and retry */ }
}
} catch (e) { /* network error */ } Prevention
- Build request URLs through a typed helper that asserts required query params
- Keep query parameter names in a shared constant between client and controller
- Make truly optional params nullable or defaulted in the controller signature
- Add an integration test asserting each endpoint's required query params
When it happens
Trigger: A controller method declares e.g. function list(#[MapQueryParameter] int $page) and the request URL lacks ?page=; or the parameter name differs from the argument name and no custom name is set via #[MapQueryParameter(name: '...')]; or the client sends the parameter in the POST body instead of the query string; or a nullable/defaulted argument exists but isVariadic with no value present in an unsupported combination.
Common situations: Frontend forgot to append the query parameter on an API call; a link/template builds a URL without the parameter; a rename of the query key in the client was not mirrored in the controller (or vice versa); clients sending data as form body while the controller expects query string; reverse proxies or redirects stripping the query string.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Invalid 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/b9d24fb2abc61606.
Report an issue: GitHub.
Appendix: source
Thrown at Controller/ArgumentResolver/QueryParameterValueResolver.php:50
* @author Ionut Enache <i.ovidiuenache@yahoo.com>
*/
final class QueryParameterValueResolver implements ValueResolverInterface, SourceValueResolverInterface
{
public function resolve(Request $request, ArgumentMetadata $argument): array
{
if (!$attribute = $argument->getAttributesOfType(MapQueryParameter::class)[0] ?? null) {
return [];
}
$name = $attribute->name ?? $argument->getName();
$validationFailedCode = $attribute->validationFailedStatusCode;
if (!$request->query->has($name)) {
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;
}View on GitHub (pinned to aa3a39d728)