{"record":{"id":"49f342ee1b270338","repo":"MHSanaei/3x-ui","slug":"request-failed-with-status-status","errorCode":null,"errorMessage":"Request failed with status ${status}","messagePattern":"Request failed with status (.+?)","errorType":"http","errorClass":"HttpError","httpStatus":null,"severity":"error","filePath":"frontend/src/api/http-init.ts","lineNumber":193,"sourceCode":"  if (res.status === 403 && !SAFE_METHODS.has(method.toUpperCase())) {\n    csrfToken = null;\n    const fresh = await fetchCsrfToken();\n    if (fresh) {\n      csrfToken = fresh;\n      res = await performFetch(method, url, data, options, fresh);\n    }\n  }\n\n  if (res.status === 401) {\n    if (!sessionExpired) {\n      sessionExpired = true;\n      window.location.replace(window.X_UI_BASE_PATH || basePathPrefix || '/');\n    }\n    return new Promise<HttpResponse>(() => {});\n  }\n\n  const parsed = await parseBody(res);\n  if (!res.ok) throw new HttpError(res.status, res.statusText, parsed);\n  return { ok: true, status: res.status, statusText: res.statusText, data: parsed };\n}\n\nexport function setupHttp(): void {\n  let basePath: string | null | undefined = window.X_UI_BASE_PATH;\n  if (!basePath) {\n    const metaTag = document.querySelector('meta[name=\"base-path\"]');\n    basePath = metaTag ? metaTag.getAttribute('content') : null;\n  }\n  basePathPrefix =\n    typeof basePath === 'string' && basePath !== '' && basePath !== '/'\n      ? basePath.replace(/\\/$/, '')\n      : '';\n\n  csrfToken = readMetaToken();\n}\n","sourceCodeStart":175,"sourceCodeEnd":210,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/frontend/src/api/http-init.ts#L175-L210","documentation":"HttpError is thrown by the shared httpRequest wrapper in frontend/src/api/http-init.ts after any response where res.ok is false (status outside 200-299) that was not already handled as 401 (session-expiry redirect) or retried as 403 (CSRF refresh). The message template 'Request failed with status ${status}' is the generic HttpError message; the thrown value carries status, statusText, and the parsed body so callers can branch on it. It is the single choke point for all panel REST failures, so server-side error messages arrive as err.data (often {msg: ...}).","triggerScenarios":"Any POST/GET to /panel/api/* returning 4xx/5xx: submitting an inbound with invalid data (400 with JSON body), hitting a route behind session auth after cookie loss but before the 401 handler fires on a safe method, 500s from panicking handlers, or gateway timeouts. Safe-method CSRF 403s are retried once; a second 403 falls through to this throw.","commonSituations":"Form validation errors surfaced from Gin ShouldBind; expired sessions on non-redirected requests; backend route changed but frontend stale (dev server not restarted after rebuild); reverse proxy returning 502 while the panel restarts.","solutions":["Inspect the thrown HttpError: read err.status and err.data.msg — the Go side almost always puts the human-readable reason in data.msg","If status is 403 repeatedly, confirm the CSRF meta tag / X-CSRF-Token flow is intact (setupHttp ran before requests)","If 401s leak through on safe methods, verify window.X_UI_BASE_PATH matches the serving base path so the redirect lands on the login page","For 5xx, check the Go panel logs for the matching request error"],"exampleFix":"// before\nconst res = await httpRequest('POST', '/panel/api/inbounds/add', payload);\n\n// after\ntry {\n  const res = await httpRequest('POST', '/panel/api/inbounds/add', payload);\n} catch (e) {\n  if (e instanceof HttpError) {\n    message.error(String((e.data as { msg?: string })?.msg ?? e.statusText));\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"export function isHttpError(e: unknown): e is HttpError {\n  return e instanceof HttpError && typeof e.status === 'number';\n}","tryCatchPattern":"try {\n  await httpRequest('POST', url, payload);\n} catch (e) {\n  if (isHttpError(e)) {\n    if (e.status === 401) return; // redirect already handled\n    message.error(String((e.data as { msg?: string })?.msg ?? e.statusText));\n    return;\n  }\n  throw e; // network failure etc.\n}","preventionTips":["Always read err.data.msg — the backend puts the actionable reason there","Run setupHttp() before any request so CSRF and base path are initialized","Wrap mutations in one shared error toast helper instead of per-call alerts"],"tags":["http","frontend","typescript","api-client"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}