Validation Loop error occurred when allOf used with discriminator

Issue #269 resolved
Kübra Zeray created an issue

Hello,

I’ve been implementing request validation by using OpenApiInteractionValidator for a schema with inheritance. I’m using allOf key word with discriminator but getting the following error:

{
"errorMessage": "[Path '/content/0/basket/fruits/0'] Validation loop: schema "#/components/schemas/Apple" visited twice for pointer "/content/0/basket/fruits/0" of validated instance",
"errorCode": "validation.request.body.schema.processingError"
}

I tried to find an answer and applied all suggestions especially in FAQ and in issues. I also tried with pet.yaml file, applying with post, but still getting same error.

Example yaml for the case:

/basket:
    post:
      summary: Create a basket, and optionally put some fruit into it.
      operationId: createBasket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BasketResource'
      responses:
        '201':
          description: Basket has been created, and all initial fruits were successfully given
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BasketResult'
components:
  schemas:
    BasketResource:
      type: object
      required:
        - fruits
      properties:
        fruits:
          $ref: '#/components/schemas/FruitResource'

    FruitResource:
      type: object
      required:
        - fruits
      properties:
        rewards:
          type: array
          items:
            $ref: '#/components/schemas/Fruit'
    Fruit:
      type: object
      required:
        - fruitType
      properties:
        rewardType:
          type: string
          enum:
            - APPLE
            - ORANGE
      discriminator:
        propertyName: fruitType
        mapping:
          APPLE: AppleResource
          ORANGE: OrangeResource

    Apple:
      allOf:
        - $ref: '#/components/schemas/Fruit'
        - type: object
        properties:
          colour:
            type: string
          variety:
            type: string
        required:
          - colour
          - variety

    Orange:
      allOf:
        - $ref: '#/components/schemas/Fruit'
        - type: object
          properties:
            originCountry:
              type: string
          required:
            - originCountry

Version used:

swagger-request-validator-core version: 2.8.3

Validator creation:OpenApiInteractionValidator.createFor(s).withLevelResolver(LevelResolverFactory.withAdditionalPropertiesIgnored()).build();

Hope this is simple enough explanation. Please ask for further info if needed.

Comments (26)

  1. Reza Nirumand Account Deactivated

    We faced also this issue. Also the mapper does not work really.

    We use spring-boot 2 and also swagger-request-validator-springmvc version 2.9.0

  2. Amon Amon

    This is my case and it;s because of the mapping:

    Parent:
      required:      
        - childId
      type: object
      properties:     
        childId:
          type: string
          enum:       
            - test                     
          example: "test"          
      discriminator:
        propertyName: childId
        mapping:
          test: "#/components/schemas/Test
    
    Test:
      description: "Just a child of Parent."      
      allOf:
        - $ref: "#/components/schemas/Parent" 
    
    
    Because mapping is pointing to Test it complains. 
    

  3. Kübra Zeray reporter

    Hello Amon,

    Yes it is easy to ignore this error by implementing the setting you shared, but we’ve just realised ignoring it actually causes a problem. By considering the first example I’d given when I opened this ticket (basket with/without fruits)

    • I send a basket with fruits, all valid (all required fields exist etc). I could see the validation loop exception but the flow continues since I ignore it. So that’s OK.
    • I send an invalid basket with an orange (originCountry is missing), I do not get a proper validation error as I do expect. There is no validation for the fields takes place. The worst is, If you didn’t implement any null check or so since you think you filter the request and validate them, unfortunately you’re wrong. You’ll get NPE in your flow.

  4. Przemek Brzosko

    Hi,

    @James Navin when do you plan to release new version with this issue fixed?

    I have attached a sample project above. I have downloaded 286-269-discriminator-improvements and compiled the library and attach to my project, but I still got the loop validation error when sending following POST:

    {
        "vehicles": [
            {
                "type": "Car",
                "power": 100,
                "make": "mitsubishi"
            },
            {
                "type": "Train",
                "power": 10000,
                "cars": 100
            },
            {
                "type": "Plane",
                "power": 1000,
                "manufacturer": "boeing"
            }
        ]
    }
    

    It is the same with or without discriminator mapping.

    Please give me some feedback, as it blocks our project 😞

    Regards

  5. James Navin

    Hi Przemek,

    I'm planning to release a version with the changes from that branch soon - I'm just waiting to land a couple of other PRs first. I can’t commit to a date yet, but within the next week or so.

    Thanks for attaching the sample - I’ll test it against that branch and hopefully be able to get a fix for it.

    Cheers,

    James

  6. satya Kumar

    @James Navin Hi James

    I get same error as below

    Caused by: com.atlassian.oai.validator.restassured.OpenApiValidationFilter$OpenApiValidationException: 
    {
      "messages" : [ {
        "key" : "validation.response.body.schema.processingError",
        "level" : "ERROR",
        "message" : "[Path '/contentSlots/0/components/0'] Validation loop: schema \"#/components/schemas/CMSParagraphComponent\" visited twice for pointer \"/contentSlots/0/components/0\" of validated instance",
        "context" : {
          "requestPath" : "/v1/cms/pages",
          "responseStatus" : 200,
          "location" : "RESPONSE",
          "requestMethod" : "GET"
        }
      } ]
    }
    

    It will be of great help if you can get your fix merged. Thanks so much

  7. James Navin

    @Przemek Brzosko I finally got some time to look at your specific case. Its interesting and a little challenging to solve in the current implementation :)

    The validation loop error is coming out of the underlying schema validation library - it maintains a validation stack to detect loops so you don’t enter an infinite cycle. The problem with the way the allOf + discriminator works is that it needs a validation loop by design [parent type (initial ref) → sub type (discriminator ref) → parent type (allOf include) ]. The validator works in that scenario because I can control the stack within the discriminator validation, but in your case there is the additional layer of [sub type (oneOf ref) → parent type (allOf ref) → sub type (discriminator ref) → parent type (allOf ref)] which triggers the loop.

    Ptr URI Validator
    ""
    /vehicles #/components/schemas/Data/properties/vehicles InstanceValidator
    /vehicles #/components/schemas/Data/properties/vehicles/items InstanceValidator
    /vehicles/0 #/components/schemas/Data/properties/vehicles/items/oneOf/0 OneOfValidator
    /vehicles/0 #/components/schemas/Car/allOf/0 AllOfValidator
    /vehicles/0 #/components/schemas/Car Discriminator
    /vehicles/0 #/components/schemas/Car/allOf/0 Discriminator

    One thing I will look at is short-circuiting the discriminator validation if the starting point is the sub-type (e.g. inspect the stack and see if the sub-type has already been visited).

    Thanks for providing the test case!

  8. satya Kumar

    @James Navin

    Hi James thanks so much for taking up the issue. I know you did say its going to take time as its a complicated issue.

    Just wanted to check if there are any branches we can try to help you with some testing, if issue fixed. 🙂

  9. Abhay.Kumar

    Solutions is already given above to ignore this error.

    return com.atlassian.oai.validator.whitelist.ValidationErrorsWhitelist.create()
    .withRule("<put description>",
    allOf(
            methodIs(HttpMethod.<put here GET/POST/??>),
            pathContainsSubstring("<put URI>"),
            messageHasKey("validation.request.body.schema.processingError"),
            messageContains("Validation loop: schema")
    ))
    

  10. Kumaran Mani

    Hi Abhay, The solution of whitelisting does not work as mentioned by Kübra Zeray in above thread.

  11. James Navin

    Hi all,

    A colleague has done some work on this and come up with a fix. The repro test case is now green.

    Fix is available in v2.25.0

    I’ll mark this issue as resolved, but if you still encounter the problem please re-open with an example to reproduce and we will look at it again.

    Thanks for the patience.

    James

  12. Tamas Toth

    @James Navin

    Hi James,

    I have the below schema which gives me "Validation loop: schema "#/components/schemas/Car/_discriminatorValidation" visited twice for pointer "" of validated instance" error upon validation with v2.25.0 of my response in case of a even a very basic Car like {"id":0,"name":null,"type":"car","luxury":false}.

    I am new to Open API so it can easily happen that my schema uses the discriminator incorrectly, however my understanding on the Open API documentation is that it can be used this way.

    I would really appreciate some expert opinion.

    Thanks,

    Tamas

    openapi: 3.0.1
    info:
      title: REST API
      version: v1
    paths:
      "/vehicle":
        get:
          tags:
          - Vehicles
          operationId: getVehicle
          responses:
            '200':
              description: Success|OK
              content:
                "*/*":
                  schema:
                    oneOf:
                    - "$ref": "#/components/schemas/Car"
                    - "$ref": "#/components/schemas/Plane"
    components:
      schemas:
        Plane:
          type: object
          allOf:
          - "$ref": "#/components/schemas/Vehicle"
          - type: object
            properties:
              manufacturer:
                type: string
        Car:
          type: object
          allOf:
          - "$ref": "#/components/schemas/Vehicle"
          - type: object
            properties:
              isLuxury:
                type: boolean
        Vehicle:
          required:
          - type
          type: object
          properties:
            id:
              type: integer
              format: int64
            name:
              type: string
            type:
              type: string
              enum:
              - car
              - plane
          discriminator:
            propertyName: type
            mapping:
              car: "#/components/schemas/Car"
              plane: "#/components/schemas/Plane"
    

  13. James Navin

    Hi Tamas,

    Can you please try with the latest version of the library. There were further improvements added to address this problem in v2.25.1.

    Please let me know if you still experience problems with the latest version (2.27.0)

    Thanks,

    James

  14. Tamas Toth

    @James Navin With both v2.25.1 and v2.27.0 the validation hangs for a while then ends with an OutOfMemoryError with below stack trace. I increased the the heap size up to 8GB to see where it goes but that did not solve anything.

    java.lang.OutOfMemoryError: Java heap space
        at java.base/java.util.LinkedHashMap.newNode(LinkedHashMap.java:256)
        at java.base/java.util.HashMap.putVal(HashMap.java:627)
        at java.base/java.util.HashMap.put(HashMap.java:608)
        at com.fasterxml.jackson.databind.node.ObjectNode.deepCopy(ObjectNode.java:57)
        at com.fasterxml.jackson.databind.node.ObjectNode.deepCopy(ObjectNode.java:19)
        ....
        at com.fasterxml.jackson.databind.node.ObjectNode.deepCopy(ObjectNode.java:57)
        at com.fasterxml.jackson.databind.node.ObjectNode.deepCopy(ObjectNode.java:19)
        at com.fasterxml.jackson.databind.node.ObjectNode.deepCopy(ObjectNode.java:57)
        at com.atlassian.oai.validator.schema.keyword.DiscriminatorKeywordValidator.validateAllOfComposition(DiscriminatorKeywordValidator.java:206)
        at com.atlassian.oai.validator.schema.keyword.DiscriminatorKeywordValidator.doValidate(DiscriminatorKeywordValidator.java:101)
    

    Thanks,

    Tamas

  15. James Navin

    Thanks for reporting. I’ll raise a separate ticket to track this bug. At first glance it looks like our approach to tracking the validation stack is failing for your case.

  16. Log in to comment