Skip to content

stdhttp: mixed path parameter and literal suffix generates invalid ServeMux pattern #2488

Description

@Stanzilla

Version

  • oapi-codegen: v2.8.0
  • Go: go1.26.5
  • Generator: std-http-server

Minimal reproduction

Configuration:

package: api
output: server.gen.go
generate:
  std-http-server: true

OpenAPI specification:

openapi: 3.0.3
info:
  title: Custom action reproduction
  version: 1.0.0
paths:
  /resources/{resourceId}:apply:
    post:
      operationId: applyResource
      parameters:
        - name: resourceId
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Applied

The generated std-http-server registration is equivalent to:

m.HandleFunc(
	http.MethodPost+" "+options.BaseURL+"/resources/{resourceId}:apply",
	wrapper.ApplyResource,
)

Registering that pattern with http.ServeMux panics:

panic: parsing "POST /resources/{resourceId}:apply": at offset 16: bad wildcard segment (must end with '}')

This can also be reproduced without generated code:

mux := http.NewServeMux()
mux.HandleFunc(
	"POST /resources/{resourceId}:apply",
	func(http.ResponseWriter, *http.Request) {},
)

Although Go 1.22+ supports whole-segment wildcards, a mixed wildcard and literal
segment is not valid. Registering /resources/{resourceId} instead accepts the
request, but PathValue("resourceId") returns abc:apply for
/resources/abc:apply; ServeMux does not strip the action suffix.

Expected behavior

For std-http-server, either:

  1. generate a compatible route that matches the whole segment, validates and
    strips the literal suffix, and passes abc as resourceId; or
  2. reject this path shape during generation with a clear unsupported-route
    error instead of generating code that panics during server startup.

Literal suffixes on templated path segments are useful for custom action routes
such as {resourceId}:apply.

Additional context

The v2.8.0 trailing-slash and handler-registration-order fixes do not cover this
path shape. A local workaround is to register
/resources/{resourceIdAction}, dispatch based on the :apply suffix, and set
the original resourceId path value before invoking the generated wrapper.

The minimal reproduction snippets in this issue may be used under Apache-2.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions