Wei-Shaw/sub2api · error

HTTP error! status

Error message

HTTP error! status: ${response.status}

What it means

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.

Solutions

  1. Parse the response body for an error message before throwing, mirroring the i18n approach used for noResponseBody.
  2. On 401/403, redirect to admin re-login and stop the spinner cleanly.
  3. Ensure ADMIN_UI_REQUEST_HEADER is actually set on this request in the deployed environment (check proxy header forwarding).
  4. Add specific messages per status (401/403/429/5xx) instead of the generic template.

Example fix

// before
if (!response.ok) {
  throw new Error(`HTTP error! status: ${response.status}`)
}

// after
if (!response.ok) {
  const body = await response.json().catch(() => null)
  const msg = body?.error?.message || body?.message
  if (response.status === 401 || response.status === 403) {
    await reauthenticateAdmin()
    throw new Error(t('admin.accounts.grok.unauthorized'))
  }
  throw new Error(msg ? `${msg} (HTTP ${response.status})` : t('admin.accounts.grok.httpError', { status: response.status }))
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!localStorage.getItem('auth_token')) { redirectToAdminLogin(); } // before opening the admin test modal

Try / catch

try { await adminStreamTest(); }
catch (e) {
  const status = String(e?.message).match(/status: (\d+)$/)?.[1];
  if (status === '401' || status === '403') { await reauthenticateAdmin(); return; }
  showError(parseDetail(e));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/b9b9d2641a149817. Report an issue: GitHub.

Appendix: source

Thrown at frontend/src/components/admin/account/AccountTestModal.vue:896

    }

    // Use the configured API base; EventSource does not support POST.
    const url = buildApiUrl(`/admin/accounts/${props.account.id}/test`)

    // Use fetch with streaming for SSE since EventSource doesn't support POST
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
        'Content-Type': 'application/json',
        [ADMIN_UI_REQUEST_HEADER]: '1'
      },
      body: JSON.stringify(requestBody),
      signal: abortController.signal
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const reader = response.body?.getReader()
    if (!reader) {
      throw new Error(t('admin.accounts.grok.noResponseBody'))
    }

    const decoder = new TextDecoder()
    let buffer = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const lines = buffer.split('\n')
      buffer = lines.pop() || ''

View on GitHub (pinned to 073e92d171)