Skip to content

fix(rust): interpolate unknown path parameters without their JSON quotes - #17661

Open
wiebren wants to merge 1 commit into
fern-api:mainfrom
wiebren:fix/rust-string-path-parameters
Open

fix(rust): interpolate unknown path parameters without their JSON quotes#17661
wiebren wants to merge 1 commit into
fern-api:mainfrom
wiebren:fix/rust-string-path-parameters

Conversation

@wiebren

@wiebren wiebren commented Sep 4, 2026

Copy link
Copy Markdown

Description

Linear ticket: n/a — found while running one OpenAPI document through fern's SDK generators
and comparing what each one produces.

An unknown-typed path parameter is generated as &serde_json::Value and interpolated into
the path with Display — which prints JSON. A string value keeps its quotes, so the quotes
travel into the URL and every call to the endpoint fails:

pub async fn get(
    &self,
    domain_name: &serde_json::Value,
    ...
) -> Result<Domain, ApiError> {
    self.http_client.execute_request(
        Method::GET,
        &format!("domains/{}", domain_name),   // -> domains/"example.com"
GET /domains/%22example.com%22   (expected: GET /domains/example.com)

OpenAPI documents commonly hit this: a path parameter whose schema carries constraints but no
type

in: path
name: domainName
required: true
schema:
  maxLength: 255
  minLength: 3
  pattern: "^..."

— imports as unknown. Other generators shrug this off because their unknown is the
language's plain value (unknown in typescript, Any in python) and interpolating the
runtime string produces the string; rust's serde_json::Value is the one representation
whose Display re-encodes to JSON.

Root cause

getPathParameterExpression in generators/rust/sdk/src/generators/SubClientGenerator.ts
returns the parameter name unchanged for the unknown type reference, so format!("{}", value)
uses serde_json::Value's JSON Display.

Changes Made

  • For an unknown path parameter, interpolate
    value.as_str().map(ToString::to_string).unwrap_or_else(|| value.to_string()): the string
    content when the value is a string, the JSON encoding otherwise (numbers and booleans print
    bare, which is what a path expects).
  • Add the rust-unknown-path-parameter test definition (language-prefixed, so only the rust
    generators run it): an endpoint with an unknown path parameter.

Testing

  • Verified against a live socket with the generated fixture crate, calling
    client.get(&serde_json::json!("abc"), None) and capturing the request line:
request line
before GET /resources/%22abc%22 HTTP/1.1
after GET /resources/abc HTTP/1.1
  • cargo build + cargo test pass on the new fixture (86 tests).
  • pnpm seed test --generator rust-sdk on path-parameterized fixtures (path-parameters,
    imdb) — no output changes; no committed fixture has an unknown path parameter, so only
    the new fixture's output is added.
  • pnpm turbo run test --filter @fern-api/rust-sdk — all unit tests pass.

Generated with Claude Code


Devin Review

An `unknown`-typed path parameter is generated as `&serde_json::Value` and was
interpolated into the path with `Display`, which prints JSON. A string value
keeps its quotes, so every call went to `/resources/%22abc%22` instead of
`/resources/abc`. OpenAPI documents commonly hit this: a path parameter whose
schema carries constraints (`pattern`, `minLength`) but no `type` imports as
`unknown`, and every path-parameterized call in the generated SDK 404s.

Use the string content when the value is a string and fall back to the JSON
encoding otherwise (numbers and booleans print bare, which is what a path
expects).

Adds the rust-unknown-path-parameter seed fixture (language-prefixed) covering
an endpoint with an unknown path parameter. Verified against a live socket:
the request line was `GET /resources/%22abc%22` before and is
`GET /resources/abc` after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ

@nitpickybot nitpickybot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Review Summary

Single-line generator fix that unwraps serde_json::Value strings before interpolating them into paths, plus a new seed fixture. The approach is sound; the main gap is that path parameters are still not URL-encoded (a value containing / or ? still escapes the path segment), but that's pre-existing behavior beyond this diff. No blocking issues found.

  • 🔵 1 suggestion(s)

To request another review, comment /ai-review on this pull request.

Comment on lines +1718 to +1719
unknown: () =>
`${paramName}.as_str().map(ToString::to_string).unwrap_or_else(|| ${paramName}.to_string())`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 suggestion

as_str().map(ToString::to_string) allocates twice for the string case (&str -> String). Minor, but map(str::to_owned) is the same length and one allocation. Also worth noting: like every other path param here, the result is still not percent-encoded, so a value containing / or ? will break out of the segment — pre-existing, but this change makes string values reachable where they previously produced quoted-and-encoded garbage.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment on lines +1718 to +1719
unknown: () =>
`${paramName}.as_str().map(ToString::to_string).unwrap_or_else(|| ${paramName}.to_string())`,

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.

🔴 Unknown aliases break generated SDK builds

When a path parameter aliases unknown, getPathParameterExpression bypasses the new conversion and passes the alias to format!. Generated aliases lack Display, so the entire SDK fails to compile.

Prompt for agents
Extend path-parameter serialization in generators/rust/sdk/src/generators/SubClientGenerator.ts so named aliases resolve recursively before choosing the expression. An alias whose eventual target is unknown must serialize its .0 serde_json::Value like a direct unknown value. Preserve primitive-alias handling and account for alias chains. Add a fixture endpoint using an alias of unknown as a path parameter and verify the generated Rust crate compiles and removes JSON quotes from string values.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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