Skip to content

fix: handle duplicate path parameters in OpenAPI specs - #2220

Merged
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/deduplicate-path-params
Feb 15, 2026
Merged

fix: handle duplicate path parameters in OpenAPI specs#2220
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/deduplicate-path-params

Conversation

@mromaszewicz

Copy link
Copy Markdown
Member

Some real-world OpenAPI specs (e.g. Keycloak) reuse the same path parameter more than once in a single URI, such as: /clients/{client-uuid}/roles/{role-name}/composites/clients/{client-uuid}

Previously this caused a "has 4 positional parameters, but spec has 3 declared" error because SortParamsByPath compared raw URI placeholder count against unique declared parameters.

Fix this by deduplicating the path parameters extracted from the URI (preserving first-occurrence order) before matching them against the spec declared parameters. This is the right level to fix the issue rather than scattering dedup logic across template helpers.

Fixes #1574
Supersedes #2175

Some real-world OpenAPI specs (e.g. Keycloak) reuse the same path
parameter more than once in a single URI, such as:
/clients/{client-uuid}/roles/{role-name}/composites/clients/{client-uuid}

Previously this caused a "has 4 positional parameters, but spec has 3
declared" error because SortParamsByPath compared raw URI placeholder
count against unique declared parameters.

Fix this by deduplicating the path parameters extracted from the URI
(preserving first-occurrence order) before matching them against the
spec declared parameters. This is the right level to fix the issue
rather than scattering dedup logic across template helpers.

Fixes oapi-codegen#1574
Supersedes oapi-codegen#2175

Co-Authored-By: Junior Rantila <junior.rantila@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mromaszewicz
mromaszewicz requested a review from a team as a code owner February 14, 2026 01:27
@jamietanna jamietanna added this to the v2.6.0 milestone Feb 15, 2026
@mromaszewicz
mromaszewicz merged commit 1650807 into oapi-codegen:main Feb 15, 2026
26 checks passed
@jamietanna jamietanna added the bug Something isn't working label Feb 27, 2026
mromaszewicz added a commit to sterligov/oapi-codegen that referenced this pull request Aug 1, 2026
A path parameter may appear more than once in one path, as in the
Keycloak-style /admin/realms/{realm}/clients/{client-uuid}/roles/{role-name}/composites/clients/{client-uuid}
that motivated oapi-codegen#2220. SortParamsByPath deduplicates such parameters and
the client template declares one variable per deduplicated parameter,
but GenPathString numbered variables by occurrence, so the second
{client-uuid} referred to a pathParam that was never declared and the
generated client failed to compile.

Key the variable index by parameter name so repeats resolve back to the
first occurrence, and treat the prefixed and exploded forms {.param} and
{;param*} as the same parameter, matching OrderedParamsFromUri.

This also corrects the output rather than only restoring compilation.
The previous fmt.Sprintf form emitted one %s per occurrence but only one
argument per unique parameter, so the generated code failed go vet and
put a literal %!s(MISSING) in the request path at runtime.
mromaszewicz added a commit that referenced this pull request Aug 1, 2026
feat(client): build request paths by concatenation instead of fmt.Sprintf

Generated clients built operationPath with fmt.Sprintf and one %s per path
parameter. The format string is fully known at generation time, so the
formatting machinery is pure overhead at runtime; plain concatenation
produces the same string without the allocation.

    BenchmarkFmtPath1param-10       21433833    49.99 ns/op   16 B/op   1 allocs/op
    BenchmarkConcatPath1param-10    73644105    16.27 ns/op    0 B/op   0 allocs/op
    BenchmarkFmtPath2param-10       18985250    62.78 ns/op   16 B/op   1 allocs/op
    BenchmarkConcatPath2param-10    55796110    21.45 ns/op    0 B/op   0 allocs/op
    BenchmarkFmtPath3param-10       15205623    79.05 ns/op   24 B/op   1 allocs/op
    BenchmarkConcatPath3param-10    41759646    28.44 ns/op    0 B/op   0 allocs/op

ReplacePathParamsWithStr gives way to GenPathString, which emits the whole
concatenation expression rather than a format string. It builds that
expression from the regexp match offsets, quoting each literal segment with
strconv.Quote and keying parameter variables by name. That also fixes
several cases where the format-string form produced wrong output:

  - A percent sign in a path was read as a format verb, so a legal
    percent-encoded path such as /search/%20/{id} generated a corrupt
    string literal. A literal %s was worse still: it was counted as a
    parameter, yielding a reference to a variable that was never declared.

  - Literal segments were interpolated into the generated source unquoted,
    so a path containing a double quote or a backslash produced code that
    either did not compile or escaped incorrectly.

  - A path parameter repeated within a single path, as in the Keycloak-style
    path that motivated #2220, emitted one %s per occurrence but only one
    argument per unique parameter. The result failed go vet and put a
    literal %!s(MISSING) into the request path at runtime. Repeats now
    resolve to the variable bound to the first occurrence, and the prefixed
    and exploded forms {.param} and {;param*} are recognised as the same
    parameter.

The generated API surface is unchanged. Only function bodies differ, along
with the fmt import where it is no longer needed.


---------

Co-authored-by: Sterligov Denis <d.sterligov@tinkoff.ru>
Co-authored-by: mromaszewicz <marcinr@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generator confused by duplicated path parameter

2 participants