symfony/http-kernel · error · BadRequestHttpException

Request payload contains invalid

Error message

Request payload contains invalid "%s" property.

What it means

When deserializing a request payload mapped via #[MapRequestPayload], the serializer encountered an UnexpectedPropertyException - the JSON/body contains a property that does not exist on the target DTO and strict property handling is active. Symfony converts it to a 400 with the offending property name. This guards typed DTOs against unexpected/misspelled fields.

Solutions

  1. Remove the unknown property from the client payload or fix its spelling to match the DTO.
  2. Add the missing property to the DTO (or a #[SerializedName]) if the field is legitimate.
  3. Set serializationContext on the attribute: ['allow_extra_attributes' => true] to ignore extra fields.
  4. Version your API so old clients keep sending payloads compatible with the deployed DTO.

Example fix

// before
#[MapRequestPayload] UserDto $dto
// after: tolerate extra fields
#[MapRequestPayload(serializationContext: ['allow_extra_attributes' => true])] UserDto $dto
Defensive patterns

Strategy: type-guard

Validate before calling

const allowed = new Set(['name','email']);
const extra = Object.keys(payload).filter(k => !allowed.has(k));
if (extra.length) throw new Error('Unknown fields: ' + extra.join(','));

Try / catch

try {
    $result = $api->submit($payload);
} catch (BadRequestHttpException $e) {
    // message names the offending property; strip it and retry
    $logger->warning('Unexpected property sent', ['msg' => $e->getMessage()]);
}

Prevention

When it happens

Trigger: Client sends JSON with a key that has no matching property on the mapped DTO class while the serializer context enables strict/ignore-false behavior (e.g. denormalize with 'allow_extra_attributes' => false or default strict behavior in recent serializer versions).

Common situations: API clients sending extra metadata fields; renamed DTO properties after an upgrade while clients still send old field names; typos in field names; Symfony 7.x stricter serializer defaults.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13). Data as JSON: /api/errors/eda386649aea3519. Report an issue: GitHub.

Appendix: source

Thrown at Controller/ArgumentResolver/RequestPayloadValueResolver.php:290

        if (\is_array($data)) {
            $data = $this->mergeParamsAndFiles($data, $request->files->all());

            return $this->serializer->denormalize($data, $type, self::hasNonStringScalar($data) ? $format : 'csv', $attribute->serializationContext + self::CONTEXT_DENORMALIZE + ('form' === $format ? ['filter_bool' => true] : []));
        }

        if ('form' === $format) {
            throw new BadRequestHttpException('Request payload contains invalid "form" data.');
        }

        try {
            return $this->serializer->deserialize($data, $type, $format, self::CONTEXT_DESERIALIZE + $attribute->serializationContext);
        } catch (UnsupportedFormatException $e) {
            throw new UnsupportedMediaTypeHttpException(\sprintf('Unsupported format: "%s".', $format), $e);
        } catch (NotEncodableValueException $e) {
            throw new BadRequestHttpException(\sprintf('Request payload contains invalid "%s" data.', $format), $e);
        } catch (UnexpectedPropertyException $e) {
            throw new BadRequestHttpException(\sprintf('Request payload contains invalid "%s" property.', $e->property), $e);
        }
    }

    private function mapUploadedFile(Request $request, ArgumentMetadata $argument, MapUploadedFile $attribute): UploadedFile|array|null
    {
        if ($files = $request->files->get($attribute->name ?? $argument->getName())) {
            return !\is_array($files) && $argument->isVariadic() ? [$files] : $files;
        }

        if ($argument->isNullable() || $argument->hasDefaultValue()) {
            return null;
        }

        return 'array' === $argument->getType() ? [] : null;
    }

    private function mergeParamsAndFiles(array $params, array $files): array
    {

View on GitHub (pinned to aa3a39d728)