Redoc: Error: Incompatible types in allOf at "undefined"

Created on 9 May 2019  路  19Comments  路  Source: Redocly/redoc

Using 2.0.0-rc.4 with an OpenAPI spec 3.0.2 file, parsing fails and I'm getting the following error in the browser console:

redoc.standalone.js:26397 Error: Incompatible types in allOf at "undefined"
at e.mergeAllOf (redoc.standalone.js:38564)
at redoc.standalone.js:38614
at Array.map ()
at r (redoc.standalone.js:38613)
at e.hoistOneOfs (redoc.standalone.js:38625)
at e.mergeAllOf (redoc.standalone.js:38537)
at e.mergeAllOf (redoc.standalone.js:38568)
at redoc.standalone.js:38861
at Array.map ()
at e.initOneOf (redoc.standalone.js:38859)

I'm not able to understand the point in which fails.

My OpenAPI spec 3.0.2 json file validates correctly using a tool like oas-validate from https://github.com/Mermade/oas-kit

bug

All 19 comments

Could you share your spec or at least minimal reproducible sample?

Looks like you are allOfing schemas with different type somewhere in the spec, e.g.:

allOf:
  - type: string
  - type: array
    items:
      type: string

Hi @RomanGotsiy,

you can reproduce the issue with the following complete spec:

{
  "openapi": "3.0.2",
  "info": { "title": "Server REST API v3", "version": "7.0.0-dev1" },
  "components": {
    "schemas": {
      "errorResponse": {
        "type": "object",
        "properties": { "error": { "type": "integer", "minimum": 100 }, "message": { "type": "string" }, "info": { "type": "string" } },
        "required": ["error", "message"]
      },
      "data_collection": {
        "description": "Data Collection",
        "type": "object",
        "allOf": [{ "$ref": "#/components/schemas/hasAccountId" }],
        "properties": {
          "fields": {
            "type": "array",
            "items": {
              "anyOf": [
                { "$ref": "#/components/schemas/metaField" },
                { "$ref": "#/components/schemas/stringField" },
                { "$ref": "#/components/schemas/selectField" },
                { "$ref": "#/components/schemas/numberField" },
                { "$ref": "#/components/schemas/ratingField" },
                { "$ref": "#/components/schemas/booleanField" }
              ]
            }
          }
        }
      },
      "hasAccountId": {
        "type": "object",
        "required": ["acct_id"],
        "properties": { "acct_id": { "description": "Account ID", "type": "string", "readOnly": true } }
      },
      "nonEmptyString": { "type": "string", "minLength": 1 },
      "abstractField": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "id": { "$ref": "#/components/schemas/nonEmptyString" },
          "type": { "$ref": "#/components/schemas/nonEmptyString" },
          "labelId": { "$ref": "#/components/schemas/nonEmptyString" },
          "format": { "$ref": "#/components/schemas/nonEmptyString" }
        }
      },
      "metaField": {
        "type": "object",
        "allOf": [{ "$ref": "#/components/schemas/abstractField" }],
        "oneOf": [
          { "type": "object", "properties": { "format": { "enum": ["break"] } } },
          {
            "type": "object",
            "required": ["id"],
            "properties": { "format": { "enum": ["message"] }, "message": { "$ref": "#/components/schemas/nonEmptyString" } }
          },
          { "type": "object", "required": ["id", "labelId"], "properties": { "format": { "enum": ["section"] }, "implicit": { "type": "boolean" } } }
        ],
        "properties": { "type": { "enum": ["meta"] } }
      },

      "dataField": {
        "type": "object",
        "allOf": [{ "$ref": "#/components/schemas/abstractField" }],
        "properties": {
          "placeholderId": { "type": "string", "description": "label id for a placeholder text to display to the user when no value is provided" },
          "required": { "type": "boolean", "description": "For the Data Collection to be valid, a value must be provided" },
          "hidden": { "type": "boolean", "description": "If true, the field is not displayed to the agent", "default": false },
          "editable": { "type": "boolean", "description": "If true, the agent can modify the value of the field" },
          "defaultConstant": {
            "oneOf": [{ "type": "string" }, { "type": "number" }, { "type": "boolean" }],
            "description": "A default value, maybe overridden by a variable or the user"
          },
          "defaultVariableId": {
            "type": "string",
            "description": "Id of the variable to use to fill the field. If no value is retrieved, defaultConstant is used instead"
          },
          "editIfDefault": {
            "type": "boolean",
            "description": "If true, the field is displayed and can be edited by the user even if a value was set by either a variable or a constant",
            "default": false
          }
        }
      },
      "stringField": {
        "type": "object",
        "required": ["type"],
        "allOf": [{ "$ref": "#/components/schemas/dataField" }],
        "properties": {
          "type": { "type": "string", "enum": ["string"] },
          "format": {
            "type": "string",
            "enum": [
              "text",
              "textarea",
              "email",
              "nickname",
              "firstname",
              "lastname",
              "phonenum",
              "link",
              "date",
              "time",
              "date-time",
              "dropdown",
              "userid",
              "avatar",
              "intprefix",
              "street",
              "city",
              "province",
              "region",
              "state",
              "country"
            ],
            "default": "text"
          },
          "defaultConstant": { "type": "string", "description": "A default value, maybe overridden by a variable or the user" },
          "minLength": { "type": "integer" },
          "maxLength": { "type": "integer" },
          "validation": { "type": "string", "description": "Regular expression to validate the field, not applied to default values" }
        }
      },
      "selectField": {
        "type": "object",
        "required": ["type"],
        "allOf": [{ "$ref": "#/components/schemas/dataField" }],
        "properties": {
          "type": { "enum": ["dropdown"] },
          "options": { "type": "object", "additionalProperties": { "type": "string", "description": "Label id of the option" }, "minProperties": 1 }
        }
      },
      "numberField": {
        "type": "object",
        "required": ["type"],
        "allOf": [{ "$ref": "#/components/schemas/dataField" }],
        "properties": {
          "type": { "type": "string", "enum": ["number"] },
          "format": { "type": "string", "enum": ["number", "rating"], "default": "number" },
          "defaultConstant": { "type": "number", "description": "A default value, maybe overridden by a variable or the user" },
          "min": { "type": "integer" },
          "max": { "type": "integer" }
        }
      },
      "ratingField": {
        "type": "object",
        "required": ["format", "style"],
        "allOf": [{ "$ref": "#/components/schemas/numberField" }],
        "properties": { "format": { "enum": ["rating"] }, "style": { "type": "string" } }
      },
      "booleanField": {
        "type": "object",
        "required": ["type"],
        "allOf": [{ "$ref": "#/components/schemas/dataField" }],
        "properties": {
          "type": { "type": "string", "enum": ["boolean"] },
          "format": { "type": "string", "enum": ["checkbox", "radio"], "default": "checkbox" },
          "defaultConstant": { "type": "boolean", "description": "A default value, maybe overridden by a variable or the user" },
          "trueLabel": { "type": "string", "description": "Id of the string describing the \"true\" value" },
          "falseLabel": { "type": "string", "description": "Id of the string describing the \"false\" value" },
          "validation": { "type": "boolean", "description": "Value that the field must have for the Data Collection to be valid" }
        }
      }
    },
    "responses": {
      "defaultError": {
        "description": "Default/generic error response",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/errorResponse" } } }
      },
      "notFound": {
        "description": "The requested/specified resource was not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/errorResponse" } } }
      }
    },
    "parameters": {
      "id": { "description": "Unique identifier of the resource", "name": "id", "in": "path", "schema": { "type": "string" }, "required": true },
      "limit": {
        "name": "limit",
        "in": "query",
        "description": "Maximum number of items to return",
        "schema": { "type": "integer", "default": 20, "minimum": 1, "maximum": 100 }
      },
      "skip": {
        "name": "skip",
        "in": "query",
        "description": "Skip the specified number of items",
        "schema": { "type": "integer", "default": 0, "minimum": 0 }
      },
      "fields": {
        "name": "fields",
        "in": "query",
        "description": "Return only the specified properties",
        "schema": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
      },
      "sort": {
        "name": "sort",
        "in": "query",
        "description": "Sorting criteria, using RQL syntax (a,-b,+c)",
        "schema": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
      },
      "query": {
        "name": "q",
        "in": "query",
        "description": "Return only items matching the specified [RQL](https://github.com/persvr/rql) query. This parameter can also be used to specify the ordering criteria of the results",
        "schema": { "type": "string" }
      },
      "version": { "description": "Resource version", "name": "version", "in": "path", "schema": { "type": "integer" }, "required": true }
    },
    "securitySchemes": {
      "basic": { "type": "http", "scheme": "basic" }
    }
  },
  "paths": {
    "/data-collections": {
      "get": {
        "operationId": "DataCollection.query",
        "tags": ["DataCollection"],
        "responses": {
          "200": {
            "description": "List of matching DataCollections",
            "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/data_collection" } } } },
            "headers": {
              "Link": { "description": "Data pagination links, as described in RFC5988. Currently only rel=next is supported", "schema": { "type": "string" } },
              "Results-Matching": { "description": "Total number of resources matching the query", "schema": { "type": "integer", "minimum": 0 } },
              "Results-Skipped": {
                "description": "Number of resources skipped to return the current batch of resources",
                "schema": { "type": "integer", "minimum": 0 }
              }
            }
          },
          "default": { "$ref": "#/components/responses/defaultError" }
        },
        "summary": "Retrieve a list of DataCollections",
        "parameters": [
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/skip" },
          { "$ref": "#/components/parameters/fields" },
          { "$ref": "#/components/parameters/sort" },
          { "$ref": "#/components/parameters/query" }
        ],
        "security": [{ "basic": [] }]
      },
      "post": {
        "operationId": "DataCollection.create",
        "tags": ["DataCollection"],
        "responses": {
          "201": {
            "description": "DataCollection successfully created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/data_collection" } } },
            "headers": { "Location": { "description": "URI of the newly created resource", "schema": { "type": "string", "format": "uri" } } }
          },
          "default": { "$ref": "#/components/responses/defaultError" }
        },
        "summary": "Create a new DataCollection",
        "requestBody": {
          "description": "DataCollection to be created, omitting  the metadata",
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/data_collection" } } },
          "required": true
        },
        "security": [{ "basic": [] }]
      }
    }
  },
  "tags": [{ "name": "DataCollection", "description": "Data Collection Resource", "x-id": "id", "x-summary-fields": ["id", "labelId"] }],
  "servers": [{ "url": "https://my-foo-server.test.com/api/v3" }]
}

This looks like an issue in ReDoc with allOf merging.

You have a pretty complex schema. I think I've fixed it locally but the fix is a bit dangerous so I wanna test it carefully.

Hopefully will land in the upcoming release

I am facing the same issue, is there a way I can get the local fix you made? so that I can test it on my local branch?

@sujaybhowmick, Would be simpler if you just share your spec or minimal reproducible sample.

```{
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "ReDoc Bug",
"description": "ReDoc Bugn"
},
"servers": [
{
"url": "some server",
"description": "some description"
}
],
"tags": [
{
"name": "MyAPI",
"description": "Some API"
}
],
"paths": {
"/myapi": {
"post": {
"tags": [
"MyAPI"
],
"summary": "",
"description": "some descriptionn",
"parameters": [
{
"name": "Authorization",
"in": "header",
"description": "Authorization Header Base64 encoded String",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"pramam1": {
"type": "string",
"enum": [
"password"
],
"description": "Grant type supported by OAuth implementationn"
},
"param2": {
"type": "string",
"description": "some param 2n"
},
"param3": {
"type": "string",
"description": "some descriptionn"
}
}
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"type": "string",
"properties": {
"name": {
"type": "string"
}
}
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
}
}
}
}
}
}
}
}


Error produce when trying to use redoc-cli 

Prerendering docs
Error: Incompatible types in allOf at ""
at OpenAPIParser.mergeAllOf (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:8385:23)
at new SchemaModel (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:8610:30)
at new MediaTypeModel (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:8875:38)
at /Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:8945:20
at Array.map ()
at new MediaContentModel (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:8942:45)
at new ResponseModel (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:9012:28)
at /Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:9141:24
at Array.map ()
at OperationModel.get (/Users/sujaybhowmick/.nvm/versions/node/v12.2.0/lib/node_modules/redoc-cli/node_modules/redoc/bundles/redoc.lib.js:9140:18)
```

@sujaybhowmick You have the issue in your spec:

"allOf": [
  {
    "type": "string",
    "properties": {
      "name": {
        "type": "string"
      }
    }
  },
  {
    "type": "array",
    "items": {
      "type": "string"
    }
  }
]

While this JSON schema is technically valid it is incorrect: there is no valid data that validates against this schema.

allOf means that data must validate against all of the subschemas.

In your case, if the data is string it can't be array at the same time and vice versa so your schema won't validate any data. You can try it in JSON schema validator: https://www.jsonschemavalidator.net/

Also, the usage of type: "string" and properties in the first allOf item doesn't make any sense too. This part will validate only against string and properties doesn't matter here.

I would recommend you learning JSON schema more deeply, here is the best resource I've seen.

How about changing it to

openapi: "3.0.0"
info:
  version: 1.0.0
  title: ReDoc Bug
  description: |
    ReDoc Bug
servers:
  - url: some server
    description: some description
tags:
  - name: MyAPI
    description: Some API
paths:
  '/myapi':
    post:
      tags:
        - MyAPI
      summary: >-

      description: |
        some description
      parameters:
        - name: Authorization
          in: header
          description: Authorization Header Base64 encoded String
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                pramam1:
                  type: string
                  enum: ['password']
                  description: |
                    Grant type supported by OAuth implementation
                param2:
                  type: string
                  description: |
                    some param 2
                param3:
                  type: string
                  description: |
                    some description
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      name:
                        type: string
                  - type: array
                    items:
                      type: string

This works, looks like ReDoc cannot handle mixed allOf types. I was trying to keep the example simple.

openapi: "3.0.0"
info:
  version: 1.0.0
  title: ReDoc Bug
  description: |
    ReDoc Bug
servers:
  - url: some server
    description: some description
tags:
  - name: MyAPI
    description: Some API
paths:
  '/myapi':
    post:
      tags:
        - MyAPI
      summary: >-

      description: |
        some description
      parameters:
        - name: Authorization
          in: header
          description: Authorization Header Base64 encoded String
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                pramam1:
                  type: string
                  enum: ['password']
                  description: |
                    Grant type supported by OAuth implementation
                param2:
                  type: string
                  description: |
                    some param 2
                param3:
                  type: string
                  description: |
                    some description
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                  - type: object
                    properties:
                      name:
                        type: string
                  - type: object
                    properties:
                      id:
                        type: string


Sorry I am using YAML language, the JSON equivalents for the problematic part

Working example

"responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }

Bug example

{
  "openapi": "3.0.0",
  "info": {
    "version": "1.0.0",
    "title": "ReDoc Bug",
    "description": "ReDoc Bug\n"
  },
  "servers": [
    {
      "url": "some server",
      "description": "some description"
    }
  ],
  "tags": [
    {
      "name": "MyAPI",
      "description": "Some API"
    }
  ],
  "paths": {
    "/myapi": {
      "post": {
        "tags": [
          "MyAPI"
        ],
        "summary": "",
        "description": "some description\n",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "description": "Authorization Header Base64 encoded String",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "pramam1": {
                    "type": "string",
                    "enum": [
                      "password"
                    ],
                    "description": "Grant type supported by OAuth implementation\n"
                  },
                  "param2": {
                    "type": "string",
                    "description": "some param 2\n"
                  },
                  "param3": {
                    "type": "string",
                    "description": "some description\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}

Thank you for building this and sharing the code, I will try to fix it myself if possible

Same problem here friends, I'll try evaluate that suggestions...In the SwaggerHub mydocs works perfectly.

Yeah, I switched to swaggerhub and it is really worth the money very professional and has awesome integration and response on issues.
Our customers who are very big banks also love it

Now working my redoc docs and I've understood the point here.

In the swaggerhub you can "override" properties, because of that my docs was working there. In the opposite, Redoc doesn't allow that.

So, You can't have same properties in allof schemas with different types.

@sujaybhowmick Redoc is a good free tool, but if the clients agrees to pay for it, go ahead!

Best Regards!

@thiago670, Of course, it is a good tool and is free. But if I am charging my client, I need to make sure I am able to service them for what they pay.

Any progress on this? Type override is still not supported.

Same problem!

While I generally laud strictness when defining API's, I'm running into a particular issue where I'm building a middleware in front of a vendor appliance. Most of the API's are passthrough, where my middleware is merely performing what is needed to auth the incoming request before sending it on to the upstream appliance.

As such, I selectively expose the raw swagger annotations for the endpoints that I filter that the upstream appliance itself provides. The issue is these docs are _messy_, but there are around 760 paths and 950+ definitions. To attempt to clean up the "types" on each of these models would be possible as I am generating these docs programmatically, but it would be really nice if there was maybe a flag that would enable something similar to this logic:

if L.Type && R.Type
-- if config.Strict
---- compare types, fail if incompatible
-- else
---- return R.Type
else if ! L.Type && R.Type
-- return R.Type
else if L.Type && ! R.Type
-- return L.Type

This absolutely does not solve all cases and will assuredly produce issues of its own, however I presume that _most_ of the time (as is my requirement), you'll want to use the right-hand-most value of type value conflicts when merging in an allOf block.

For me this was addressed by 6e607b9a2928b062c7705087432c0f0d88e74f5d.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

martnst picture martnst  路  3Comments

ahshum picture ahshum  路  3Comments

JonKohler picture JonKohler  路  3Comments

vietnguyen010 picture vietnguyen010  路  3Comments

gavinkalika picture gavinkalika  路  3Comments