Skip to content

[BUG][PYTHON] Exception when serializing any_of with primitive types #23409

@w1ll-i-code

Description

@w1ll-i-code

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator? (Not needed, it is a bug in the template)
  • Have you tested with the latest master to confirm the issue still exists? (Not needed, the source code is still there.)
  • 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

I have generated an openapi python client with the python template from my openapi definition. I have built the openapi spec with the openapi-generator-cli container. Then I have tried using the api client in a python script:

from openapi_generator_bug.api_client import ApiClient
from openapi_generator_bug.models.id import Id
from openapi_generator_bug import DefaultApi

client = ApiClient()
api = DefaultApi(client)
api.get(id=[Id(1)])

This however resulted in a validation error

~/Documents/test/openapi-generator-bug 
.venv 0 $ python script.py 
Traceback (most recent call last):
  File "/home/william/Documents/test/openapi-generator-bug/script.py", line 7, in <module>
    api.get(id=[Id(1)])
    ~~~~~~~^^^^^^^^^^^^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/pydantic/_internal/_validate_call.py", line 39, in wrapper_function
    return wrapper(*args, **kwargs)
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/pydantic/_internal/_validate_call.py", line 136, in __call__
    res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api/default_api.py", line 83, in get
    _param = self._get_serialize(
        id=id,
    ...<3 lines>...
        _host_index=_host_index
    )
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api/default_api.py", line 281, in _get_serialize
    return self.api_client.param_serialize(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        method='GET',
        ^^^^^^^^^^^^^
    ...<10 lines>...
        _request_auth=_request_auth
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 241, in param_serialize
    query_params = self.sanitize_for_serialization(query_params)
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 364, in sanitize_for_serialization
    self.sanitize_for_serialization(sub_obj) for sub_obj in obj
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 367, in sanitize_for_serialization
    return tuple(
           ^^^^^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 368, in <genexpr>
    self.sanitize_for_serialization(sub_obj) for sub_obj in obj
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 364, in sanitize_for_serialization
    self.sanitize_for_serialization(sub_obj) for sub_obj in obj
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 394, in sanitize_for_serialization
    for key, val in obj_dict.items()
                    ^^^^^^^^^^^^^^
AttributeError: 'int' object has no attribute 'items'

this happens, because the method to_dict can return a primitive type, if the openapi model wrapping it is only used for validation (as in my case for the oneOf), but the method sanitize_for_serialization assumes it to always be a dict and calls .items() on it

openapi-generator version

openapi-generator-cli:v7.21.0

This seems to be a bug in the implementation.

OpenAPI declaration file content or url
{
  "openapi": "3.0.0",
  "info": {
    "title": "openapi generator bug",
    "version": "0.1.0"
  },
  "paths": {
    "/get": {
      "get": {
        "operationId": "get",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Id"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Id": {
        "oneOf": [
          {
            "type": "string",
            "enum": ["none"]
          },
          {
            "type": "integer",
            "format": "uint64",
            "minimum": 1.0
          }
        ]
      }
    }
  }
}
Generation Details
podman run --rm \
    -v "${PWD}":/app:z \
    docker.io/openapitools/openapi-generator-cli:v7.21.0@sha256:ce308310f3c1f8761e65338b8ab87b651bf4862c6acb80de510f381fffc4510b \
    generate \
    -i /app/openapi-generator-bug/schema.json \
    -g python \
    -o /app/openapi-generator-bug/python \
    --additional-properties=packageName="openapi_generator_bug", \
    --additional-properties=projectName="OpenAPI Generator Bug", \
    --additional-properties=packageVersion="0.1.0" \
    --additional-properties=generateSourceCodeOnly="false"
Steps to reproduce
  1. Create the spec file with the above spec
  2. Run the command specified above to generate the library
  3. Create a venv python -m venv .venv
  4. Activate the venv source .venv/bin/activate
  5. Install the library pip install openapi-generator-bug/python
  6. Copy and run the script from above.
Related issues/PRs

None found.

Suggest a fix

This is the problem source:
https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/python/api_client.mustache#L402

The primitive type however originates from here:
https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/python/model_oneof.mustache#L197

You can simply move some code around in the template to make this work:

        elif isinstance(obj, dict):
            return {
                key: self.sanitize_for_serialization(val)
                for key, val in obj.items()
            }

        # Convert model obj to dict except
        # attributes `openapi_types`, `attribute_map`
        # and attributes which value is not None.
        # Convert attribute name to json key in
        # model definition for request.
        if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
            obj_dict = obj.to_dict()
        else:
            obj_dict = obj.__dict__

         return self.sanitize_for_serialization(obj_dict)

Tried this solution locally and it works (for my use-case).

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