Skip to content

Type-confusion IRI guard drops violation property path and expected type (regression from #8333) #8352

Description

@alexndlm

API Platform version(s) affected: 4.3.14 (regression introduced in #8333, also present on the current 4.3 branch after #8339)

Description

The type-confusion guard in AbstractItemNormalizer::getResourceFromIri() throws a bare NotNormalizableValueException without a deserialization path or expected type. When COLLECT_DENORMALIZATION_ERRORS is enabled (the default for the DeserializeProvider), DeserializeProvider::createViolationFromException() then builds a ConstraintViolation with an empty property path and an empty expected type, so the user-facing violation degrades to This value should be of type . with no property name.

Before #8333 the guard threw an InvalidArgumentException, which was caught a few lines below and re-thrown via NotNormalizableValueException::createForUnexpectedDataType(...) carrying both deserialization_path and the expected type — so the violation correctly pointed at the offending property. #8333 changed the throw to a NotNormalizableValueException (so union/intersection denormalization can fall through to the next member), but in doing so it stopped going through the factory, dropping that metadata for the single-type case.

Reproduction

Resource with a relation printingHouse typed PrintingHouse, POST a valid IRI that points to a different resource type:

POST /books
{ "title": "t", "printingHouse": "/publishing-houses/1" }

(/publishing-houses/1 resolves to a PublishingHouse, while the relation is declared as PrintingHouse.)

Actual (4.3.14)

{
  "violations": [
    { "propertyPath": "", "message": "This value should be of type ." }
  ]
}

propertyPath and the expected type are lost.

Expected (4.3.13 behaviour)

{
  "violations": [
    { "propertyPath": "printingHouse", "message": "Invalid IRI \"/publishing-houses/1\"." }
  ]
}

Cause

src/Serializer/AbstractItemNormalizer.php, in getResourceFromIri() (line 768-770 in 4.3.14, line 775-777 on 4.3 after #8339):

if (!$matchesType) {
    throw new NotNormalizableValueException(\sprintf('The iri "%s" does not reference the correct resource.', $data));
}

This bypasses the createForUnexpectedDataType() factory used by the neighbouring ItemNotFoundException / InvalidArgumentException branches, so getPath() and getExpectedTypes() are null on the resulting exception.

Possible solution

Keep it a NotNormalizableValueException (so the union/intersection fallback added in #8333 / #8339 still works), but build it through the factory so the path and expected type are preserved:

if (!$matchesType) {
    throw NotNormalizableValueException::createForUnexpectedDataType(
        \sprintf('Invalid IRI "%s".', $data),
        $data,
        [$resourceClass],
        $context['deserialization_path'] ?? null,
        true
    );
}

I have a fix + unit test ready and can open a PR against 4.3 if this looks good.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions