Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions web/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,28 @@ api.interceptors.response.use(
return api(original)
},
)

// Guard against 2xx responses that aren't JSON. Every endpoint this client
// talks to returns JSON, so an HTML body means the request never reached the
// API: the gateway has no route for that path, so it fell through to the SPA
// and nginx answered index.html with a 200. Without this, axios resolves those
// as success and callers read fields off an HTML string — a missing gateway
// route surfaces as a silently blank value instead of an error.
api.interceptors.response.use((response) => {
const contentType = response.headers["content-type"]
const hasBody =
response.status !== 204 && response.data !== "" && response.data != null
if (hasBody && typeof contentType === "string" && !contentType.includes("json")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject responses whose content type is missing

When a 2xx response has a nonempty body but omits Content-Type—for example, due to another misconfigured gateway or proxy—the typeof contentType === "string" condition skips this guard entirely, so HTML or another string is still resolved as a successful API response and recreates the silent failure this change is intended to prevent. Treat a missing header as invalid whenever hasBody is true; the existing body check already allows genuinely empty responses.

Useful? React with 👍 / 👎.

return Promise.reject(
new AxiosError(
`Expected JSON from ${response.config.url ?? "the API"} but got "${contentType}" ` +
`(HTTP ${response.status}). The API gateway is probably missing a route for this path.`,
AxiosError.ERR_BAD_RESPONSE,
response.config,
response.request,
response,
),
)
}
return response
})
Loading