{"record":{"id":"e59c9d05107d25ea","repo":"prometheus/prometheus","slug":"res-statustext","errorCode":null,"errorMessage":"res.statusText","messagePattern":"res\\.statusText","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/ui/mantine-ui/src/api/api.ts","lineNumber":57,"sourceCode":"      const startTime = Date.now();\n\n      const res = await fetch(\n        `${pathPrefix}/${API_PATH}${path}${queryString}`,\n        {\n          cache: \"no-store\",\n          credentials: \"same-origin\",\n          signal,\n        }\n      );\n\n      if (\n        !res.ok &&\n        !res.headers.get(\"content-type\")?.startsWith(\"application/json\")\n      ) {\n        // For example, Prometheus may send a 503 Service Unavailable response\n        // with a \"text/plain\" content type when it's starting up. But the API\n        // may also respond with a JSON error message and the same error code.\n        throw new Error(res.statusText);\n      }\n\n      const apiRes = (await res.json()) as APIResponse<T>;\n\n      if (recordResponseTime) {\n        recordResponseTime(Date.now() - startTime);\n      }\n\n      if (apiRes.status === \"error\") {\n        throw new Error(\n          apiRes.error !== undefined\n            ? apiRes.error\n            : 'missing \"error\" field in response JSON'\n        );\n      }\n\n      return apiRes as SuccessAPIResponse<T>;\n    } catch (error) {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/prometheus/prometheus/blob/44d6a0e0b1dbdcba4e68853d50e0bafc87d90507/web/ui/mantine-ui/src/api/api.ts#L39-L75","documentation":"Thrown by the mantine-ui API layer when an HTTP response is both non-OK and not application/json (e.g. a plain-text 503 from a starting Prometheus). The wrapper cannot parse a JSON error body, so it surfaces the HTTP status text as the Error message. It is the catch-all for non-JSON transport-level failures.","triggerScenarios":"Any useFetch/useSuspenseAPIQuery request where res.ok is false AND the content-type header does not start with 'application/json' — typically a 503 text/plain from Prometheus during startup, a 404 HTML page from a reverse proxy, or a gateway timeout from a load balancer.","commonSituations":"Prometheus still starting (WAL replay) behind the UI; wrong --web.external-url/path prefix so the UI hits a proxy error page; ingress/LB returning HTML error pages; server restarting mid-query.","solutions":["Wait for Prometheus to finish startup and retry (check /-/ready).","Verify the UI is served by the same Prometheus instance (pathPrefix/external URL config) so fetch hits the real API, not a proxy error page.","If behind a reverse proxy, configure it to pass Prometheus responses through unchanged or return JSON errors.","Handle non-OK responses in the UI with a friendly message using res.status and res.statusText."],"exampleFix":"// before\nthrow new Error(res.statusText);\n\n// after (include status code for diagnosability)\nthrow new Error(`${res.status} ${res.statusText}`);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await fetchAPI<T>('/query', {...});\n} catch (err) {\n  if (err instanceof Error && /^[1-5]\\d\\d /.test(err.message)) {\n    // non-JSON HTTP error surface: show retry UI, do not crash\n  }\n}","preventionTips":["Gate UI data loading on /-/ready returning 200 to avoid startup 503 text responses.","Serve the UI from the same origin/prefix as the API so proxies never inject HTML error pages.","Check res.status before res.json() in custom fetch code and branch on content-type."],"tags":["http","network","api","mantine-ui"],"backgroundTag":null,"analyzedSha":"44d6a0e0b1dbdcba4e68853d50e0bafc87d90507","analyzedAt":"2026-08-15T08:44:33.427Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}