Skip to content

feat: add client-response-error-on-unexpected-response output option - #2540

Open
ChrisJr404 wants to merge 1 commit into
oapi-codegen:mainfrom
ChrisJr404:client-response-error-on-unexpected-response
Open

feat: add client-response-error-on-unexpected-response output option#2540
ChrisJr404 wants to merge 1 commit into
oapi-codegen:mainfrom
ChrisJr404:client-response-error-on-unexpected-response

Conversation

@ChrisJr404

Copy link
Copy Markdown

Summary

Closes #1923.

When you only declare a couple of the responses you care about, the generated Parse<Operation>Response today silently returns a response object with just Body/HTTPResponse set (and a nil error) for anything the spec didn't mention, so there's no easy way to notice an unexpected response.

This adds an opt-in output-options flag, client-response-error-on-unexpected-response, that appends a default case to the response switch returning a new ErrUnexpectedResponse sentinel:

switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
    var dest Thing
    if err := json.Unmarshal(bodyBytes, &dest); err != nil {
        return nil, err
    }
    response.JSON200 = &dest
default:
    return nil, ErrUnexpectedResponse
}

var ErrUnexpectedResponse = errors.New("unexpected response") is emitted alongside the client, so callers can errors.Is(err, ErrUnexpectedResponse).

It's off by default, so existing generated code is unchanged (confirmed by re-running make generate with no diffs elsewhere). New internal/test/options/unexpected_response covers both the enabled path (undeclared status returns the sentinel, a declared 200 still parses fine) and the default path (undeclared status still returns a response with a nil error).

Docs and configuration-schema.json updated to match.

When enabled, the generated Parse<Operation>Response functions get a
default case that returns a new ErrUnexpectedResponse sentinel when the
response status code and content-type match none of the responses declared
in the spec. Off by default, so existing output is unchanged.
@ChrisJr404
ChrisJr404 requested a review from a team as a code owner August 25, 2026 09:38
@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an opt-in output option that makes generated response parsers return a shared sentinel for undeclared status/content-type combinations.

  • Adds the configuration field, schema entry, and documentation.
  • Adds conditional sentinel and default-branch generation to response-aware clients.
  • Adds enabled and default-behavior generation fixtures and tests.

Confidence Score: 3/5

The PR should not merge until response parsers honor the option for operations without decoder clauses and the new sentinel cannot collide with generated declarations.

Valid specifications can either produce uncompilable Go through an ErrUnexpectedResponse name collision or silently retain the old nil-error behavior because response generation exits before adding the configured default branch.

Files Needing Attention: pkg/codegen/template_helpers.go, pkg/codegen/templates/client-with-responses.tmpl

Important Files Changed

Filename Overview
pkg/codegen/template_helpers.go Adds the unexpected-response default branch, but existing early returns prevent it from being generated for operations without decoder clauses.
pkg/codegen/templates/client-with-responses.tmpl Adds the sentinel declaration, but its fixed package-level identifier can collide with generated schema declarations.
pkg/codegen/configuration.go Adds the opt-in output option with a YAML key and documented default behavior.
configuration-schema.json Keeps the JSON configuration schema synchronized with the new output option.
internal/test/options/unexpected_response/enabled/unexpected_response_test.go Covers a declared JSON response and an undeclared status, but not empty-decoder operations or generated-name collisions.
Prompt To Fix All With AI
### Issue 1
pkg/codegen/templates/client-with-responses.tmpl:22
**Sentinel collides with schema names**

When the option is enabled for a specification containing a component whose generated name is `ErrUnexpectedResponse`, this fixed package-level variable duplicates the generated type identifier, causing the generated Go package to fail compilation with a redeclaration error.

### Issue 2
pkg/codegen/template_helpers.go:281-283
**Empty decoders bypass unexpected errors**

When the option is enabled for an operation whose responses produce no decoder clauses, `genResponseUnmarshal` returns before emitting this default branch, so an undeclared status still returns a response with a nil error instead of `ErrUnexpectedResponse`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat: add client-response-error-on-unexp..." | Re-trigger Greptile

// ErrUnexpectedResponse is returned by the Parse<Operation>Response functions
// when the server responds with a status code and content-type that match none
// of the responses declared in the OpenAPI specification.
var ErrUnexpectedResponse = errors.New("unexpected response")

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.

P1 Sentinel collides with schema names

When the option is enabled for a specification containing a component whose generated name is ErrUnexpectedResponse, this fixed package-level variable duplicates the generated type identifier, causing the generated Go package to fail compilation with a redeclaration error.

Knowledge Base Used: Template rendering system

Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/codegen/templates/client-with-responses.tmpl
Line: 22

Comment:
**Sentinel collides with schema names**

When the option is enabled for a specification containing a component whose generated name is `ErrUnexpectedResponse`, this fixed package-level variable duplicates the generated type identifier, causing the generated Go package to fail compilation with a redeclaration error.

**Knowledge Base Used:** [Template rendering system](https://app.greptile.com/oapi-codegen/-/custom-context/knowledge-base/oapi-codegen/oapi-codegen/-/docs/template-rendering.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +281 to +283
if globalState.options.OutputOptions.ClientResponseErrorOnUnexpectedResponse {
fmt.Fprintf(buffer, "default:\nreturn nil, ErrUnexpectedResponse\n")
}

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.

P1 Empty decoders bypass unexpected errors

When the option is enabled for an operation whose responses produce no decoder clauses, genResponseUnmarshal returns before emitting this default branch, so an undeclared status still returns a response with a nil error instead of ErrUnexpectedResponse.

Knowledge Base Used: Operation and client generation

Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/codegen/template_helpers.go
Line: 281-283

Comment:
**Empty decoders bypass unexpected errors**

When the option is enabled for an operation whose responses produce no decoder clauses, `genResponseUnmarshal` returns before emitting this default branch, so an undeclared status still returns a response with a nil error instead of `ErrUnexpectedResponse`.

**Knowledge Base Used:** [Operation and client generation](https://app.greptile.com/oapi-codegen/-/custom-context/knowledge-base/oapi-codegen/oapi-codegen/-/docs/operation-and-client-generation.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

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.

Add an output option for generating a default case in response parsing function

1 participant