GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — echo: don't echo Go error messages into HTTP responses by paulo-raca · Pull Request #2370 · oapi-codegen/oapi-codegen Conversation
|
| location | value |
|---|---|
| path | ctx.Param("X") |
| query | ctx.QueryParams().Get("X") |
| header | valueList[0] |
| cookie | cookie.Value |
For pkg/codegen/templates/echo/v5/echo-wrappers.tmpl, .SetInternal(err) becomes .Wrap(err) (v5's equivalent — *echo.HTTPError doesn't have SetInternal, but it does have a Wrap(err) method that attaches an inner error).
Out of scope for this PR. The chi / gorilla / fiber / gin / iris / stdhttp templates have the same shape (http.Error(w, err.Error(), …)), and the same argument applies, but I want this PR to be small and easy to review. Happy to do those in follow-ups if this lands.
Internal / wrapped) so logging is unaffected.Regenerated all the affected test fixtures (internal/test/parameters/echo, internal/test/parameters/echov5, internal/test/strict-server/echo, the petstore-expanded examples). go test ./... is clean across the root, examples, and internal/test modules.
We've shipped this as a local template override for over a year and it's surfaced no issues — the change is small, the contract is preserved, and the request/response shape is unchanged for valid traffic. Upstreaming so we don't have to maintain the override.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The echo wrapper template emits four parameter-bind error paths
(path / query / header / cookie) that currently surface the raw
runtime/Bind*ParameterWithOptions error into the HTTP response:
return echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("Invalid format for parameter X: %s", err))
The string returned by err.Error() is implementation detail of
runtime/types/parsing — it can include internal struct field names,
package-local error wording, or library version-dependent text.
Production servers shouldn't echo that to API consumers.
Replace each with:
// echo (v4)
return echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("Invalid format for parameter X: '%s'", <user-value>),
).SetInternal(err)
// echo v5 — same idea, different API:
return echo.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("Invalid format for parameter X: '%s'", <user-value>),
).Wrap(err)
The HTTP body now reflects what the caller sent (`ctx.Param(...)` /
`ctx.QueryParams().Get(...)` / `valueList[0]` / `cookie.Value`), and
the original err is preserved on the echo HTTPError as either
SetInternal (v4) or Wrap (v5) so it still flows to server logs but
never to the wire.
Regenerated test fixtures and examples to match.
Greptile SummaryThis PR changes the echo and echo-v5 server templates so that 400 Bad Request error responses no longer include the raw Go error string — instead they echo back the user-supplied value in quotes (e.g.
Confidence Score: 3/5The template logic is correct and generated fixtures are internally consistent, but the change unconditionally rewrites the HTTP 400 response body for all echo users without a migration path. The four error-message sites in both echo templates are updated correctly and symmetrically, and all regenerated files match the templates with no unrelated drift. However, every generated echo server will emit a different 400 body on the next codegen run — clients, integration tests, or monitoring rules that pattern-match on the previous error wording will silently break. The project's convention is to gate unconditional wire-format changes behind a compatibility or output flag, which is absent here. There is also no new behavioral test to lock in the new format going forward. pkg/codegen/templates/echo/echo-wrappers.tmpl and its v5 counterpart are the source of truth; the generated files are fine on their own.
|
| Filename | Overview |
|---|---|
| pkg/codegen/templates/echo/echo-wrappers.tmpl | Core source of all changes: replaces %s err-in-message with '%s' user-value + .SetInternal(err) for path/query/header/cookie error paths; unconditional behavior change with no compatibility flag. |
| pkg/codegen/templates/echo/v5/echo-wrappers.tmpl | Echo v5 equivalent of the same four-site change; uses .Wrap(err) instead of .SetInternal(err) matching v5's API — correct and symmetric with the v4 template. |
| internal/test/parameters/echo/gen/server.gen.go | All 44 error-message sites regenerated consistently with the template; no unrelated drift observed, but no new behavior test accompanies the fixture update. |
| internal/test/parameters/echov5/server.gen.go | Echo v5 parameter test fixtures regenerated symmetrically with v4; changes are consistent with the template. |
| examples/petstore-expanded/echo/api/petstore-server.gen.go | Petstore echo example regenerated; 4 error-path lines updated correctly, no unrelated changes. |
| examples/petstore-expanded/echo-v5/api/petstore-server.gen.go | Petstore echo-v5 example regenerated; 4 error-path lines updated with .Wrap(err) correctly. |
Reviews (1): Last reviewed commit: "echo: don't put Go err.Error() into HTTP..." | Re-trigger Greptile
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if err != nil { | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: '%s'", ctx.Param("{{.ParamName}}"))).SetInternal(err) | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Unconditional behavior change without a compatibility flag
This modifies the HTTP 400 response body for every generated echo server unconditionally. Per the project's practice, any change to generated output that alters observable wire behavior should be opt-in via a new compatibility or output-options flag, not a silent change to all users. Downstream consumers — integration tests, API gateways checking error bodies, client libraries parsing these messages — will silently break without a migration path. The same applies to the query, header, and cookie error paths below (and in the v5 template). Consider gating behind a flag like output-options.echo-error-include-value (or similar) so existing deployments can keep the current behavior.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| err = runtime.BindQueryParameterWithOptions("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), ¶ms.{{.GoName}}, runtime.BindQueryParameterOptions{Type: "{{.SchemaType}}", Format: "{{.SchemaFormat}}"}) | ||
| if err != nil { | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: '%s'", ctx.QueryParams().Get("{{.ParamName}}"))).SetInternal(err) | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
QueryParams().Get() drops all but the first value for multi-value parameters
For array-typed query parameters sent as repeated keys (e.g. ?tags=foo&tags=bar), ctx.QueryParams().Get("tags") returns only "foo". When binding fails, the error message Invalid format for parameter tags: 'foo' omits bar entirely, which may be confusing when the actual submitted multi-value list was the real source of the parsing failure. Consider using ctx.QueryParams()["paramName"] joined with commas, or ctx.Request().URL.RawQuery, to give a more faithful representation of what the caller actually sent.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| var value int32 | ||
| err = runtime.BindStyledParameterWithOptions("simple", "p", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: false, Required: false, Type: "integer", Format: "int32"}) | ||
| if err != nil { | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: %s", err)) | ||
| return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: '%s'", cookie.Value)).SetInternal(err) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
No regression test verifying the new error message format
The test fixtures were regenerated, but no test was added to internal/test/parameters/echo/ (or the echo-v5 counterpart) that actually issues a malformed request and asserts the new '<value>'-style response body. Without this, a future template edit could silently revert the format without CI catching it. A table-driven test covering path/query/header/cookie bad-value cases would make the contract explicit.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
At least 1 approving review is required to merge this pull request.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.