Skip to content

fix(dev-server): regenerate API code for oapi-codegen 2.7.1 - #883

Draft
nieblara wants to merge 2 commits into
mainfrom
cursor/regenerate-oapi-codegen-b532
Draft

nieblara wants to merge 2 commits into
mainfrom
cursor/regenerate-oapi-codegen-b532

Conversation

@nieblara

@nieblara nieblara commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

This PR adds no new tests. The existing dev server tests pass, and a before/after test of the HTTP API is in the next section. I built and tested on linux/amd64 only.

Related issues

#720 updated oapi-codegen from 2.4.1 to 2.7.1. That PR did not regenerate internal/dev_server/api/server.gen.go. On main, go generate ./... rewrites the file, and then go build fails.

Describe the solution you've provided

This PR regenerates the file and fixes the build. It does not change the API behavior or the JSON wire format of the dev server.

  • Regenerate server.gen.go with the pinned oapi-codegen v2.7.1.
  • Update github.com/oapi-codegen/runtime from v1.1.2 to v1.4.2. The new generated code calls functions that first appear in v1.4.0. The oapi-codegen v2.7.0 release notes also require v1.4.0 or later. v1.4.2 fixes a required-parameter regression from oapi-codegen v2.7.0.
  • Use the new Go field names in the handlers. Variation.Id is now UnderscoreId. Project.LastSyncedFromSource is now UnderscoreLastSyncedFromSource. The JSON tags _id and _lastSyncedFromSource do not change.
  • Mark the request bodies of patchProject and postAddProject as required in api.yaml. oapi-codegen 2.7 sends a nil body to the handler when an optional body is empty. Both handlers read the body without a nil check. Without this change, an empty body causes a nil pointer panic and an HTTP 500 response. With this change, the server returns the same 400 error as before. The UI and the CLI always send a JSON body to these endpoints.

The generated file has other changes that do not change the wire format:

  • New parameter binder calls that include the parameter type
  • Valid() methods on the enum types
  • http.Method* constants in the router
  • Local StrictHandlerFunc and StrictMiddlewareFunc types
  • JSON encoding into a buffer before the server writes the status code

Wire format evidence

I ran the same 48 requests against two binaries: one built from main and one built from this branch. Each binary used its own empty database. The requests cover each endpoint in api.yaml, including error cases such as bad JSON, bad query parameters, conflicts, and missing projects. Eight requests called the LaunchDarkly API with a real project. These cover add project, sync (PATCH), and the environments endpoint.

Result: 44 responses are byte-identical, including the status code, Content-Type, and Content-Length. The other 4 responses are live project responses. Their parsed JSON is equal, but the key order inside some flag values is different. The same difference occurs when I run the main binary two times. The flag values are ldvalue.Value objects, and their key order comes from Go map order, which is random.

I also built the first commit of this PR alone. On that build, PATCH /dev/projects/{key} with an empty body returned HTTP 500 because of a nil pointer panic. The second commit restores the old 400 response.

Comparison table (the live project key is shown as <live>)
 st  method  path                                                                      bytes  old vs new           old vs old
200  GET     /dev/projects                                                                 3  identical            identical
201  POST    /dev/projects/imported/import                                               326  identical            identical
409  POST    /dev/projects/imported/import                                                64  identical            identical
400  POST    /dev/projects/bad/import                                                     63  identical            identical
400  POST    /dev/projects/bad/import                                                    124  identical            identical
200  GET     /dev/projects                                                                13  identical            identical
200  GET     /dev/projects/imported                                                      326  identical            identical
200  GET     /dev/projects/imported?expand=overrides                                     399  identical            identical
200  GET     /dev/projects/imported?expand=overrides&expand=availableVariations          613  identical            identical
200  GET     /dev/projects/imported?expand=overrides,availableVariations                 326  identical            identical
200  GET     /dev/projects/imported?expand=bogus                                         326  identical            identical
500  GET     /dev/projects/missing                                                        71  identical            identical
200  PUT     /dev/projects/imported/overrides/flag-a                                      32  identical            identical
200  PUT     /dev/projects/imported/overrides/flag-c                                      41  identical            identical
400  PUT     /dev/projects/imported/overrides/flag-a                                     113  identical            identical
400  PUT     /dev/projects/imported/overrides/flag-a                                      63  identical            identical
200  GET     /dev/projects/imported?expand=overrides&expand=availableVariations          736  identical            identical
204  DELETE  /dev/projects/imported/overrides/flag-a                                       0  identical            identical
404  DELETE  /dev/projects/imported/overrides/flag-a                                       0  identical            identical
204  DELETE  /dev/projects/missing/overrides                                               0  identical            identical
404  DELETE  /dev/projects/imported/overrides                                             25  identical            identical
200  GET     /dev/projects/imported?expand=overrides                                     465  identical            identical
400  PATCH   /dev/projects/imported                                                       63  identical            identical
400  PATCH   /dev/projects/imported                                                       74  identical            identical
400  POST    /dev/projects/new-project                                                    63  identical            identical
400  POST    /dev/projects/new-project                                                    72  identical            identical
200  GET     /dev/debug-sessions                                                          51  identical            identical
200  GET     /dev/debug-sessions?limit=5&offset=0                                         51  identical            identical
400  GET     /dev/debug-sessions?limit=abc                                               116  identical            identical
200  GET     /dev/debug-sessions/nope/events?kind=feature&limit=2                         49  identical            identical
400  GET     /dev/debug-sessions/nope/events?offset=x                                    115  identical            identical
204  DELETE  /dev/debug-sessions/nope                                                      0  identical            identical
200  GET     /dev/backup                                                               28672  identical            identical
404  GET     /dev/nope                                                                    19  identical            identical
404  POST    /dev/debug-sessions                                                          19  identical            identical
200  OPTIONS /dev/projects/imported                                                        0  identical            identical
201  POST    /dev/projects/<live>?expand=availableVariations                            1979  same-json-key-order  same-json-key-order
409  POST    /dev/projects/<live>                                                         82  identical            identical
200  GET     /dev/projects/<live>/environments                                            72  identical            identical
200  GET     /dev/projects/<live>/environments?name=prod&limit=1                          43  identical            identical
400  GET     /dev/projects/<live>/environments?limit=abc                                 116  identical            identical
200  PATCH   /dev/projects/<live>?expand=overrides                                       695  same-json-key-order  same-json-key-order
200  PATCH   /dev/projects/<live>                                                        686  same-json-key-order  same-json-key-order
200  GET     /dev/projects/<live>?expand=overrides&expand=availableVariations           2000  same-json-key-order  same-json-key-order
204  DELETE  /dev/projects/imported                                                        0  identical            identical
404  DELETE  /dev/projects/imported                                                       51  identical            identical
204  DELETE  /dev/projects/<live>                                                          0  identical            identical
200  GET     /dev/projects                                                                 3  identical            identical

48 requests, 0 unexplained differences

For the backup endpoint, the test compares the status, the headers, the body length, and the first 16 bytes of the body.

Checks that I ran

  • go generate ./... two times: the second run makes no changes. This includes cmd/resources/resource_cmds.go and the mocks.
  • go mod tidy: no changes.
  • go build ./...: passes.
  • go test ./...: all 55 packages pass. If LD_ACCESS_TOKEN is set in the environment, 3 tests in cmd/flags, cmd/setup, and cmd/whoami fail. They fail the same way on main.
  • golangci-lint v1.63.4: no issues.
  • Dev server UI: npm ci, npm run lint, npm test, and npm run build pass. The build does not change dist/. This PR does not change the UI.

Describe alternatives you've considered

  • Update the runtime to the latest version (v1.7.0). I did not do this because v1.4.2 is the smallest version that works and includes the fix for oapi-codegen v2.7.0.
  • Add a nil check to the two handlers. This changes the response for an empty body, because the server would then sync or return a different error. The required: true change keeps the old response.
  • Go back to oapi-codegen 2.4.1. v2.7.1 fixes a code injection vulnerability (GHSA-rjwr-m7qx-3fjr), so I kept it.

Additional context

CI does not run go generate ./... and then check for a diff. That is why #720 merged with green CI. A drift check in CI can find this type of problem in the future.

Open in Web Open in Cursor 

cursoragent and others added 2 commits October 20, 2026 03:03
Dependabot PR #720 updated oapi-codegen from 2.4.1 to 2.7.1, but it did
not regenerate server.gen.go. Regenerate the file with the pinned
version.

The new generated code calls runtime functions that first appear in
github.com/oapi-codegen/runtime v1.4.0. Update the runtime to v1.4.2,
which also fixes a required-parameter regression from oapi-codegen 2.7.0.

The generator renamed two Go fields. Variation.Id is now UnderscoreId
and Project.LastSyncedFromSource is now UnderscoreLastSyncedFromSource.
Update the hand-written handlers to use the new names. The JSON tags
("_id" and "_lastSyncedFromSource") do not change.

Co-authored-by: rniebla <rniebla@launchdarkly.com>
oapi-codegen 2.7 accepts an empty body for an optional request body and
sends a nil Body to the handler. PatchProject and PostAddProject read
request.Body without a nil check, so an empty body caused a panic.

Mark these request bodies as required in api.yaml. The generated code
then returns the same 400 error for an empty body as before.

Co-authored-by: rniebla <rniebla@launchdarkly.com>
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.

2 participants