Skip to content

[BUG][JS] oneOf constructor ignores discriminator and always throws "Multiple Matches" #23025

@tray2100

Description

@tray2100

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

If you have a oneOf schema with a discriminator and generate a JavaScript client, the generated constructor is broken — it throws a "Multiple matches found" error every single time, even for completely valid input. The discriminator is just ignored.


Why it's broken

The template uses typeof instance === "Parrot" to check whether instance is already a constructed class object. The problem is that typeof in JavaScript only ever returns primitive type strings like "object", "string", "number", etc. — never a
class name. So that check is always false, the else branch always runs, and validateJSON gets called for every candidate on every invocation.

Since both Parrot and Finch share birdType as their only required field, both validateJSON calls pass, match hits 2, and the constructor throws — regardless of what you pass in. The birdType discriminator that was explicitly declared to
resolve this exact ambiguity is completely ignored.

openapi-generator version

org.openapitools:openapi-generator-gradle-plugin:7.20.0

OpenAPI declaration file content or url

Spec

  components:
    schemas:
      Bird:
        oneOf:
          - $ref: '#/components/schemas/Parrot'
          - $ref: '#/components/schemas/Finch'
        discriminator:
          propertyName: birdType

      Parrot:
        type: object
        properties:
          birdType:
            type: string
          speech:
            type: string
        required:
          - birdType

      Finch:
        type: object
        properties:
          birdType:
            type: string
          beakSize:
            type: integer
        required:
          - birdType

What gets generated

  constructor(instance = null) {
      if (instance === null) {
          this.actualInstance = null;
          return;
      }
      var match = 0;
      var errorMessages = [];

      try {
          if (typeof instance === "Parrot") {   // ← always false
              this.actualInstance = instance;
          } else {
              Parrot.validateJSON(instance);    // ← always runs, always passes
              this.actualInstance = Parrot.constructFromObject(instance);
          }
          match++;                              // ← always incremented
      } catch(err) {
          errorMessages.push("Failed to construct Parrot: " + err)
      }

      try {
          if (typeof instance === "Finch") {    // ← always false
              this.actualInstance = instance;
          } else {
              Finch.validateJSON(instance);     // ← always runs, always passes
              this.actualInstance = Finch.constructFromObject(instance);
          }
          match++;                              // ← always incremented
      } catch(err) {
          errorMessages.push("Failed to construct Finch: " + err)
      }

      if (match > 1) {
          throw new Error("Multiple matches found...");  // ← always throws
      }
  }
Generation Details

Leveraging the OpenAPI gradle plugin

Steps to reproduce

Use the pattern in the YAML above and you'll see that the OneOf object that gets generated has issues during deserialization when you use polymorphism.

Related issues/PRs

issue #18125 is similar but not the same

Suggest a fix

The fix is to detect if a discriminator is being used and then use it to explicitly choose the object that needs to be constructed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions