Skip to content

[Rust] Apply the isAnyType param fallback to operations without path params - #24570

Merged
wing328 merged 1 commit into
OpenAPITools:masterfrom
emilbonnek:rust-anytype-query-params-without-path-params
Aug 11, 2026
Merged

[Rust] Apply the isAnyType param fallback to operations without path params#24570
wing328 merged 1 commit into
OpenAPITools:masterfrom
emilbonnek:rust-anytype-query-params-without-path-params

Conversation

@emilbonnek

@emilbonnek emilbonnek commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #20141

The rust client generates Option<models::models::TypeName> for an enum query parameter when the operation has no path parameters. That does not compile:

error[E0433]: cannot find `models` in `models`

The same parameter on an operation that does have a path parameter generates Option<&str> and is fine. That difference is the bug.

Cause

In RustClientCodegen.postProcessOperationsWithModels, the loop that defaults isAnyType path, query and header params to String (added in #20631) sits inside if (operation.pathParams.size() > 0). The loop iterates allParams and already guards on isPathParam || isQueryParam || isHeaderParam, so the outer condition only suppresses it for operations without path params.

When it is suppressed, the param keeps a dataType that already carries the models:: prefix from AbstractRustCodegen.getTypeDeclaration, and api.mustache prefixes it again.

This only shows up when the parameter schema is a $ref the parser does not treat as a bare ref, so it is not marked isString: a $ref wrapped in anyOf with null, or a $ref with sibling keywords. Both are common in FastAPI output. A bare $ref was already fine, which is why the existing enum-query-params sample did not catch it.

The fix moves the loop out of the pathParams check, which is what #20631 describes doing.

Reproducing

{
  "openapi": "3.1.0",
  "info": { "title": "Repro", "version": "1.0.0" },
  "paths": {
    "/widgets/summary": {
      "get": {
        "operationId": "getWidgetSummary",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [ { "$ref": "#/components/schemas/WidgetKind" }, { "type": "null" } ],
              "title": "Kind"
            }
          }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WidgetSummary" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WidgetKind": { "type": "string", "enum": [ "basic", "premium" ], "title": "WidgetKind" },
      "WidgetSummary": {
        "type": "object",
        "title": "WidgetSummary",
        "properties": { "kind": { "$ref": "#/components/schemas/WidgetKind" }, "total": { "type": "integer" } },
        "required": [ "kind", "total" ]
      }
    }
  }
}
openapi-generator-cli generate -i repro.json -g rust --skip-validate-spec

Before: kind: Option<models::models::WidgetKind>, cargo build fails.
After: kind: Option<&str>, cargo build passes.

Testing

  • Regenerated all 37 rust configs. No existing sample changed, so no committed output regresses.
  • Added rust-reqwest-nullable-enum-query-params, a 3.1 spec covering both shapes with and without a path parameter. It does not compile on master and does compile with this change.
  • Rust*Test passes, 30 tests.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in Git BASH)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

cc: @frol @farcaller @richardwhiuk @paladinzh @jacob-pro


Summary by cubic

Fixes a Rust codegen bug where enum query params in operations without path params compiled as models::models::TypeName. We now always default isAnyType path/query/header params to String, so generated clients compile consistently.

  • Bug Fixes
    • Move the isAnyType fallback loop outside the path params check to apply on all operations.
    • Prevent double models:: prefix; affected params now emit as strings (e.g., Option<&str>).
    • Add an OpenAPI 3.1 spec and a reqwest sample for nullable enum query params (with/without path params); regenerate Rust samples (adds the new sample) and all Rust tests pass.

Written for commit 71bccca. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java:781">
P2: JSON-content query parameters with an unconstrained schema now become `Option<&str>` rather than `Option<serde_json::Value>` when the operation has no path params. Preserve `serde_json::Value` for `queryIsJsonMimeType` parameters so callers can still supply and serialize arbitrary JSON values.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// However for path, query, and headers it's unlikely to be JSON so we default to `String`.
// Note that we keep the default `serde_json::Value` for body parameters.
for (var param : operation.allParams) {
if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: JSON-content query parameters with an unconstrained schema now become Option<&str> rather than Option<serde_json::Value> when the operation has no path params. Preserve serde_json::Value for queryIsJsonMimeType parameters so callers can still supply and serialize arbitrary JSON values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java, line 781:

<comment>JSON-content query parameters with an unconstrained schema now become `Option<&str>` rather than `Option<serde_json::Value>` when the operation has no path params. Preserve `serde_json::Value` for `queryIsJsonMimeType` parameters so callers can still supply and serialize arbitrary JSON values.</comment>

<file context>
@@ -774,19 +774,18 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
+            // However for path, query, and headers it's unlikely to be JSON so we default to `String`.
+            // Note that we keep the default `serde_json::Value` for body parameters.
+            for (var param : operation.allParams) {
+                if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {
+                    param.dataType = "String";
+                    param.isPrimitiveType = true;
</file context>
Suggested change
if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {
if (param.isAnyType && !param.queryIsJsonMimeType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilbonnek thanks for the PR. does this suggestion make sense to you?

please update the samples when you've time

…params

The block that defaults isAnyType path, query and header params to String
was nested inside a check for the operation having path params, so it never
ran for operations that have none. Those params kept a dataType that already
carries the models:: prefix, and the api template prefixes it again, giving
models::models::TypeName which does not compile.

Fixes OpenAPITools#20141
@emilbonnek
emilbonnek force-pushed the rust-anytype-query-params-without-path-params branch from 23b0ef9 to 71bccca Compare August 10, 2026 17:52
@emilbonnek

Copy link
Copy Markdown
Contributor Author

@wing328 rebased on master and regenerated, samples should be green now. The only diff was a stray .openapi-generator-ignore line in the sample's .openapi-generator/FILES.

On cubic's suggestion, I don't think it's quite preserving existing behavior, though I may be misreading it. As far as I can tell master already lowers isAnyType JSON-content query params to String whenever the operation has a path param (from #20631), so this PR makes the two cases agree rather than changing that one.

That said the underlying point seems fair to me: a query param declared with content: application/json is arguably the one case where the "unlikely to be JSON" assumption doesn't hold. But applying it would change #20631's behavior for all operations, and I couldn't find a sample covering that shape, so I'd rather not fold it in untested. Happy to do it as a follow-up with a spec that exercises it, or add it here with a sample if you'd prefer that instead.

@wing328

wing328 commented Aug 11, 2026

Copy link
Copy Markdown
Member

Happy to do it as a follow-up with a spec that exercises it, or add it here with a sample if you'd prefer that instead.

sounds good to me.

thanks for the contribution

@wing328
wing328 merged commit 8f2032c into OpenAPITools:master Aug 11, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][Rust] Bad module import generated with Option types

2 participants