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

  1. Append the missing parameter to the request URL, e.g. /api/items?page=1
  2. Verify the query parameter name matches the controller argument name (or pass the explicit name: #[MapQueryParameter(name: 'per_page')])
  3. If the parameter is truly optional, make the argument nullable or give it a default: function list(#[MapQueryParameter] ?int $page = null)
  4. Confirm the client sends it in the query string, not the request body
  5. 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

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


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)