Skip to content

fix: fall back to schema key when two schemas share a title#1448

Open
alexander-wenzel-dev wants to merge 1 commit into
openapi-generators:mainfrom
alexander-wenzel-dev:fix/title-collision-fallback
Open

fix: fall back to schema key when two schemas share a title#1448
alexander-wenzel-dev wants to merge 1 commit into
openapi-generators:mainfrom
alexander-wenzel-dev:fix/title-collision-fallback

Conversation

@alexander-wenzel-dev
Copy link
Copy Markdown

@alexander-wenzel-dev alexander-wenzel-dev commented Jun 6, 2026

fix: fall back to schema key when two schemas share a title

Summary

When two component schemas declare the same title, the second one was
silently dropped along with every endpoint that referenced it. This PR
makes the second variant fall back to a class name derived from its
schema key so both schemas survive.

Problem

Tools like FastAPI emit duplicate title values for input and output
variants of the same model (for example Thing-Input and Thing-Output
both carrying title: Thing). The class name is derived from the title,
so the second variant collides with the first and is rejected with
Attempted to generate duplicate models. The generator emits a warning
but exits 0, so the schema and every endpoint that referenced it are
silently dropped from the generated client.

Minimal reproduction

components:
  schemas:
    Thing-Input:
      title: Thing
      type: object
      properties:
        name: { "type": "string" }
    Thing-Output:
      title: Thing
      type: object
      properties:
        name: { "type": "string" }
        id:   { "type": "string" }

Before this PR: Thing-Output and any endpoint that references it are
absent from the generated client, and the only signal is a warning on
stderr.

Fix

In ModelProperty.build, when the title-derived class name is already
taken, fall back to a class name derived from the schema key
(Thing-Output becomes ThingOutput) provided that key is unique.
The original schema's name is preserved, and both variants and their
endpoints generate.

The fallback is guarded by data.title and name so it only triggers
in the duplicate-title case. If the fallback name also collides, the
existing Attempted to generate duplicate models error path still
fires unchanged.

Tests

  • Functional test:
    end_to_end_tests/functional_tests/generated_code_execution/test_title_collisions.py
    — uses the inline spec above and asserts both Thing and
    ThingOutput exist and round-trip correctly.
  • Unit test in
    tests/test_parser/test_properties/test_model_property.py
    (TestBuild::test_model_name_conflict_fallback) exercises the new
    branch in ModelProperty.build directly.

Local verification

pdm install
pdm run ruff format . --check
pdm run ruff check .
pdm mypy --show-error-codes
pdm test_with_coverage

All green on Python 3.14. 100% coverage on the changed file
(model_property.py). Golden-record snapshot tests pass without
regeneration — no generated output changes for any existing spec in
the upstream baseline.

Risk

  • Ordering: the first variant encountered wins the title-derived
    name; the second falls back to the schema-key-derived name. For the
    motivating Thing-Input / Thing-Output case this is the desired
    result. A schema author who expected the second variant to claim
    the title would see different output, but that case previously
    errored out, so this is a strict improvement over the prior failure
    mode.
  • No public-API change. The fix is a single conditional in
    ModelProperty.build; nothing else in the parser or templates is
    touched.

Related

Companion PR: #1449 — a second independent fix for
freeform-object defaults, surfaced from the same downstream spec.
The two PRs share no code paths and can land in either order.

Tools like FastAPI emit duplicate `title` values for input and output variants
of the same model (for example `Thing-Input` and `Thing-Output` both carrying
`title: Thing`). The class name is derived from the title, so the second
variant collided with the first and was rejected with `Attempted to generate
duplicate models`. The generator emitted a warning and silently dropped the
schema along with every endpoint that referenced it.

When the title-derived class name is already taken, the second variant now
falls back to a class name derived from its schema key (`Thing-Output` becomes
`ThingOutput`) provided that key is unique. The original schema's name is
preserved, and both variants and their endpoints generate.

A functional test in `end_to_end_tests/functional_tests` covers the inline
spec, and a unit test in `tests/test_parser/test_properties/test_model_property.py`
exercises the new branch in `ModelProperty.build`.
@alexander-wenzel-dev alexander-wenzel-dev marked this pull request as ready for review June 6, 2026 05:52
alexander-wenzel-dev added a commit to alexander-wenzel-dev/mealie-mcp that referenced this pull request Jun 6, 2026
Adds a [tool.uv.sources] override resolving openapi-python-client from
alexander-wenzel-dev/openapi-python-client at branch
pin/mealie-mcp-combined. The branch carries two unmerged generator
fixes (upstream PRs openapi-generators/openapi-python-client#1448 and
openapi-generators/openapi-python-client#1449) that unblock the
Recipe-Input / Recipe-Output models and the PUT/PATCH recipe endpoints
in the generated client.

This commit only changes resolution; the generated client tree is
untouched and gets regenerated in a separate PR. Removal plan is
inline in pyproject.toml.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant