{"record":{"id":"b9b9d2641a149817","repo":"Wei-Shaw/sub2api","slug":"http-error-status-response-status-b9b9d2","errorCode":null,"errorMessage":"HTTP error! status: ${response.status}","messagePattern":"HTTP error! status: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/components/admin/account/AccountTestModal.vue","lineNumber":896,"sourceCode":"    }\n\n    // Use the configured API base; EventSource does not support POST.\n    const url = buildApiUrl(`/admin/accounts/${props.account.id}/test`)\n\n    // Use fetch with streaming for SSE since EventSource doesn't support POST\n    const response = await fetch(url, {\n      method: 'POST',\n      headers: {\n        Authorization: `Bearer ${localStorage.getItem('auth_token')}`,\n        'Content-Type': 'application/json',\n        [ADMIN_UI_REQUEST_HEADER]: '1'\n      },\n      body: JSON.stringify(requestBody),\n      signal: abortController.signal\n    })\n\n    if (!response.ok) {\n      throw new Error(`HTTP error! status: ${response.status}`)\n    }\n\n    const reader = response.body?.getReader()\n    if (!reader) {\n      throw new Error(t('admin.accounts.grok.noResponseBody'))\n    }\n\n    const decoder = new TextDecoder()\n    let buffer = ''\n\n    while (true) {\n      const { done, value } = await reader.read()\n      if (done) break\n\n      buffer += decoder.decode(value, { stream: true })\n      const lines = buffer.split('\\n')\n      buffer = lines.pop() || ''\n","sourceCodeStart":878,"sourceCodeEnd":914,"githubUrl":"https://github.com/Wei-Shaw/sub2api/blob/073e92d17178a1ccdb0a27017f572f10c9c7ab62/frontend/src/components/admin/account/AccountTestModal.vue#L878-L914","documentation":"In frontend/src/components/admin/account/AccountTestModal.vue:896 (the admin variant of the test modal), the streaming fetch checks response.ok and throws `HTTP error! status: ${response.status}` for any non-2xx. This variant sends an ADMIN_UI_REQUEST_HEADER: '1' marker, so failures commonly relate to admin authentication/authorization rather than the model itself. The response body's error detail is discarded.","triggerScenarios":"POST to the admin account-test URL returns 401 (admin token expired), 403 (non-admin user or missing ADMIN_UI_REQUEST_HEADER), 400 (invalid requestBody/model_id), or 5xx (upstream provider failure). The thrown message contains only the status number.","commonSituations":"Admin session expiring while the modal is open; the admin request header stripped by a proxy or missing in a custom deployment; testing an account whose upstream key is dead; gateway timeout on slow model responses (504).","solutions":["Parse the response body for an error message before throwing, mirroring the i18n approach used for noResponseBody.","On 401/403, redirect to admin re-login and stop the spinner cleanly.","Ensure ADMIN_UI_REQUEST_HEADER is actually set on this request in the deployed environment (check proxy header forwarding).","Add specific messages per status (401/403/429/5xx) instead of the generic template."],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(`HTTP error! status: ${response.status}`)\n}\n\n// after\nif (!response.ok) {\n  const body = await response.json().catch(() => null)\n  const msg = body?.error?.message || body?.message\n  if (response.status === 401 || response.status === 403) {\n    await reauthenticateAdmin()\n    throw new Error(t('admin.accounts.grok.unauthorized'))\n  }\n  throw new Error(msg ? `${msg} (HTTP ${response.status})` : t('admin.accounts.grok.httpError', { status: response.status }))\n}","handlingStrategy":"try-catch","validationCode":"if (!localStorage.getItem('auth_token')) { redirectToAdminLogin(); } // before opening the admin test modal","typeGuard":null,"tryCatchPattern":"try { await adminStreamTest(); }\ncatch (e) {\n  const status = String(e?.message).match(/status: (\\d+)$/)?.[1];\n  if (status === '401' || status === '403') { await reauthenticateAdmin(); return; }\n  showError(parseDetail(e));\n}","preventionTips":["Parse the response JSON error body before throwing; keep the generic string only as fallback","Verify ADMIN_UI_REQUEST_HEADER survives any reverse proxy","Handle 401/403 with a re-login flow instead of a toast"],"tags":["http","admin","fetch","streaming","error-handling","frontend"],"backgroundTag":null,"analyzedSha":"073e92d17178a1ccdb0a27017f572f10c9c7ab62","analyzedAt":"2026-08-15T14:33:00.750Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}