fix(desktop): stop calling a 3xx HTML reply a missing endpoint
The JSON guard in fetchJson/fetchPublicJson blamed every HTML reply on a missing backend endpoint. A 3xx HTML body is an access proxy redirecting to its login page: the endpoint exists, the credentials never arrived. Besides misleading the user, the wording is the capability signal that isMissingHealthEndpointError and the renderer's gateway-rpc predicate key on, so an auth redirect was silently classified as "endpoint missing" and routed onto compatibility paths instead of surfacing as an error. Build the error in api-transport.ts (where the sibling httpStatusError lives) and pick the hint by status: 3xx names the redirect and points at the saved token/extra headers; everything else keeps the endpoint-missing wording the predicates rely on. Part of #112072
This commit is contained in:
@@ -19,6 +19,7 @@ import { afterAll, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
destroyKeepaliveAgents,
|
||||
downloadAgentFor,
|
||||
htmlResponseError,
|
||||
httpStatusError,
|
||||
isIdempotentMethod,
|
||||
isTransientTransportError,
|
||||
@@ -404,3 +405,16 @@ describe('httpStatusError', () => {
|
||||
expect(httpStatusError(0, 'boom').statusCode).toBe(500)
|
||||
})
|
||||
})
|
||||
|
||||
describe('htmlResponseError', () => {
|
||||
it('names an auth redirect for 3xx HTML and keeps the endpoint-missing capability wording otherwise', () => {
|
||||
const redirected = htmlResponseError('https://gateway.example.com/api/profiles', 302).message
|
||||
|
||||
expect(redirected).toContain('status 302')
|
||||
expect(redirected).toMatch(/redirected/)
|
||||
expect(redirected).not.toContain('endpoint is likely missing')
|
||||
expect(htmlResponseError('https://gateway.example.com/api/missing', 404).message).toContain(
|
||||
'endpoint is likely missing'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -194,9 +194,29 @@ function readStatusCode(error: unknown): number {
|
||||
return Number(error && typeof error === 'object' ? (error as { statusCode?: unknown }).statusCode : NaN)
|
||||
}
|
||||
|
||||
/**
|
||||
* Error for a JSON endpoint that answered with HTML. A 2xx/404 HTML body is
|
||||
* the SPA index.html for an unregistered /api path, and downstream capability
|
||||
* probes (isMissingHealthEndpointError, gateway-rpc) key on the "endpoint is
|
||||
* likely missing" wording. A 3xx is an access proxy bouncing the request to
|
||||
* its login page instead — the endpoint exists, the credentials never
|
||||
* arrived — so it must neither carry that wording nor blame the backend.
|
||||
*/
|
||||
function htmlResponseError(url: string, statusCode: unknown) {
|
||||
const status = Number(statusCode)
|
||||
|
||||
const hint =
|
||||
status >= 300 && status < 400
|
||||
? 'The request was redirected, usually by an authentication proxy in front of the gateway; check the saved token and extra gateway headers.'
|
||||
: 'The endpoint is likely missing on the Hermes backend.'
|
||||
|
||||
return new Error(`Expected JSON from ${url} but got HTML (status ${statusCode}). ${hint}`)
|
||||
}
|
||||
|
||||
export {
|
||||
destroyKeepaliveAgents,
|
||||
downloadAgentFor,
|
||||
htmlResponseError,
|
||||
httpStatusError,
|
||||
isIdempotentMethod,
|
||||
isTransientTransportError,
|
||||
|
||||
@@ -33,6 +33,7 @@ import { classifyActiveRuntime } from './active-runtime-state'
|
||||
import {
|
||||
destroyKeepaliveAgents,
|
||||
downloadAgentFor,
|
||||
htmlResponseError,
|
||||
httpStatusError,
|
||||
jsonAgentFor,
|
||||
readStatusCode,
|
||||
@@ -5469,12 +5470,7 @@ function fetchJson(url, token, options: any = {}) {
|
||||
const contentType = String(res.headers['content-type'] || '')
|
||||
|
||||
if (looksHtml || contentType.includes('text/html')) {
|
||||
reject(
|
||||
new Error(
|
||||
`Expected JSON from ${url} but got HTML (status ${res.statusCode}). ` +
|
||||
'The endpoint is likely missing on the Hermes backend.'
|
||||
)
|
||||
)
|
||||
reject(htmlResponseError(url, res.statusCode))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -5631,12 +5627,7 @@ function fetchPublicJson(url, options: any = {}) {
|
||||
const contentType = String(res.headers['content-type'] || '')
|
||||
|
||||
if (looksHtml || contentType.includes('text/html')) {
|
||||
reject(
|
||||
new Error(
|
||||
`Expected JSON from ${url} but got HTML (status ${res.statusCode}). ` +
|
||||
'The endpoint is likely missing on the Hermes backend.'
|
||||
)
|
||||
)
|
||||
reject(htmlResponseError(url, res.statusCode))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user