fix(errorpages): answer API clients with JSON instead of an HTML page - #60
Merged
Merged
Conversation
The Traefik error-page middleware added in #52 is applied at the entrypoint level, so it wraps every router on both entrypoints — including the router for the Stackdome API server that the hub installer exposes as a StackResource. Any 5xx from the API therefore reached the dashboard as an HTML page, which axios cannot parse: the UI lost both the status and the message. Traefik copies the original request's headers onto the request it makes to the error-page service, so Accept still identifies the real caller. Serve the page to a browser navigation, and the API error envelope the dashboard already parses to everything else. The status code is unchanged either way. The match is on an explicit text/html rather than */*, because axios sends "application/json, text/plain, */*" — treating */* as a browser would reintroduce the bug for every API client. Note this cannot recover the backend's own error text: Traefik's errors middleware discards the caught response body before the error-page service is reached. Carrying the real message through needs Accept-split routers, which is a larger change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The branded Traefik error pages from #52 are wired at the entrypoint level (
charts/stackdome-agent/values.yaml), so the middleware wraps every router onwebandwebsecure. That includes the router for the Stackdome API server, which the hub installer exposes as a StackResource (install/manifests/api-server-resource-cr.yaml,exposeToPublic: true).Result: any 5xx from
/api/v1/...reached the dashboard astext/html. axios could not parse it, so the UI lost both the error message and any structured detail.Fix
Traefik copies the original request's headers onto the request it makes to the error-page service (
custom_errors.go:utils.CopyHeaders(pageReq.Header, req.Header)), soAcceptstill identifies the real caller.Accepttext/html,...application/json, text/plain, */*{"type":"Error","reason":"Internal Server Error"}AcceptheaderThe status code is untouched in every case, and the envelope matches the API's own
Errorshape (pkg/api/error_types.goin the api-server repo), sogetErrorMessage()in the dashboard reads.reasonwith no frontend change.The match is on an explicit
text/htmlrather than*/*: axios sends*/*as its last alternative, so treating that as "browser" would reintroduce the bug for every API client. There is a test entry pinning exactly that.What this does not do
It cannot return the backend's own error text. Traefik's errors middleware discards the caught response body before the error-page service is ever reached:
Recovering the real message means the middleware must not wrap API routes at all, which needs two routers per host split on
Accept(a TraefikIngressRoutematchingHeaderRegexp("Accept", "text/html")at higher priority, with the plain Ingress as passthrough). That makes the Traefik CRDs a runtime dependency of the operator and duplicates TLS config across two objects — deliberately left out of this PR.Note 4xx never reaches this middleware, so validation, auth and conflict errors have always arrived intact. This only affects 5xx.
Test plan
go build ./...— OKgo test ./internal/... ./pkg/... ./cmd/...— 16/16 packages passgofmt,go vetcleanDescribeTableininternal/errorpages/server_test.gocovers browser / axios / no-header; existing page specs now assertAccept: text/htmlexplicitlyDeploy note: the installer pins
defaultChartVersion, so a chart version bump is needed for a VPS install to pick this up.