symfony/http-kernel · error · HttpException

Missing header " ".

Error message

Missing header "%s".

What it means

Symfony's RequestHeaderValueResolver throws this HttpException when a controller argument mapped with #[MapRequestHeader] to a complex type (e.g. DateTime, Uid) is absent from the request headers, and the argument is neither nullable nor has a default value. The resolver aborts with the attribute's validationFailedStatusCode (default 422) because it cannot produce the argument value.

Solutions

  1. Send the required header with the exact name shown in the error message.
  2. Make the controller argument nullable (e.g. ?\DateTimeImmutable) so a missing header skips resolution.
  3. Give the argument a default value so absence is tolerated.
  4. Pass the header name explicitly via #[MapRequestHeader('X-Request-Id')] if the kebab-case derivation does not match what clients send.

Example fix

// before
public function show(#[MapRequestHeader] \DateTimeImmutable $lastModified) {}
// after
public function show(#[MapRequestHeader] ?\DateTimeImmutable $lastModified = null) {}
Defensive patterns

Strategy: validation

Validate before calling

// Server side: guard before relying on the argument
public function show(Request $request, #[MapRequestHeader] ?\DateTimeImmutable $lastModified = null) {
    if (null === $lastModified && $request->headers->has('last-modified') === false) {
        // handle absent header explicitly
    }
}
// Client side, before sending:
if (!isset($headers['X-Request-Id'])) {
    throw new \LogicException('Header X-Request-Id is required');
}

Type guard

function hasHeader(Request $request, string $name): bool {
    return $request->headers->has($name);
}

Try / catch

try {
    $response = $kernel->handle($request);
} catch (HttpException $e) {
    if (str_contains($e->getMessage(), 'Missing header ')) {
        return new JsonResponse(['error' => $e->getMessage()], 422);
    }
    throw $e;
}

Prevention

When it happens

Trigger: A controller declares an argument like #[MapRequestHeader] \DateTimeImmutable $lastModified or #[MapRequestHeader] Uid $requestId, and the incoming request does not contain the corresponding header (name derived from the parameter in kebab-case, e.g. 'last-modified'), while the parameter is non-nullable with no default.

Common situations: Clients omitting a mandatory header (API version, trace id); header name mismatch after renaming a controller parameter since the header name is derived from it; tests or curl calls missing -H flags; proxies or CORS preflight policies stripping custom headers.

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/aef7a71ad637b7a6. Report an issue: GitHub.

Appendix: source

Thrown at Controller/ArgumentResolver/RequestHeaderValueResolver.php:44

    {
        if (!$attribute = $argument->getAttributesOfType(MapRequestHeader::class)[0] ?? null) {
            return [];
        }

        $type = $argument->getType();
        $name = $attribute->name ?? strtolower(preg_replace('/[a-z]\K[A-Z]/', '-$0', $argument->getName()));

        if (!\in_array($type, ['string', 'array', AcceptHeader::class])) {
            if (null === $type || $argument->isVariadic() || !class_exists($type) && !interface_exists($type)) {
                throw new \LogicException(\sprintf('Could not resolve the argument typed "%s". Valid types are "array", "string" or "%s".', $type, AcceptHeader::class));
            }

            if (!$request->headers->has($name)) {
                if ($argument->isNullable() || $argument->hasDefaultValue()) {
                    return [];
                }

                throw HttpException::fromStatusCode($attribute->validationFailedStatusCode, \sprintf('Missing header "%s".', $name));
            }

            // Stage the raw header under the argument name so that a resolver able to build this type,
            // such as DateTimeValueResolver or UidValueResolver, picks it up from the attributes.
            $request->attributes->set($argument->getName(), $request->headers->get($name));

            throw new NearMissValueResolverException(\sprintf('#[MapRequestHeader] cannot build controller argument "$%s" of type "%s" by itself; no resolver converted the staged value.', $argument->getName(), $type));
        }

        $value = null;

        if ($request->headers->has($name)) {
            $value = match ($type) {
                'string' => $request->headers->get($name),
                'array' => match (strtolower($name)) {
                    'accept' => $request->getAcceptableContentTypes(),
                    'accept-charset' => $request->getCharsets(),
                    'accept-language' => $request->getLanguages(),

View on GitHub (pinned to aa3a39d728)