Wei-Shaw/sub2api · error

keyUsage.queryFailed

Error message

keyUsage.queryFailed

What it means

In frontend/src/views/KeyUsageView.vue:868, fetchUsage() calls GET {gateway}/v1/usage with a raw Bearer key. On a non-OK response it tries to parse JSON and prefers body.error.message or body.message; only when the body is missing/unparsable does it fall back to the localized 'keyUsage.queryFailed' plus the status. So this exact message means the gateway returned an error with no readable JSON body.

Source

Thrown at frontend/src/views/KeyUsageView.vue:868

  try {
    return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
  } catch {
    return 'UTC'
  }
}

// ==================== API Query ====================

async function fetchUsage(key: string) {
  const dateParams = getDateParams()
  const url = buildGatewayUrl('/v1/usage') + (dateParams ? '?' + dateParams : '')
  const res = await fetch(url, {
    headers: { 'Authorization': 'Bearer ' + key },
  })
  if (!res.ok) {
    const body = await res.json().catch(() => null)
    const msg = body?.error?.message || body?.message || `${t('keyUsage.queryFailed')} (${res.status})`
    throw new Error(msg)
  }
  return await res.json()
}

async function queryKey() {
  if (isQuerying.value) return
  const key = apiKey.value.trim()
  if (!key) {
    appStore.showInfo(t('keyUsage.enterApiKey'))
    return
  }

  isQuerying.value = true
  showResults.value = true
  showLoading.value = true
  resultData.value = null

  try {

View on GitHub (pinned to 073e92d171)

Solutions

  1. Verify buildGatewayUrl('/v1/usage') resolves to the actual gateway origin, not the SPA host.
  2. curl the URL with the same Bearer key to see the raw non-JSON body and fix the upstream (proxy 502 HTML, etc.).
  3. Include a snippet of the raw text (first 120 chars) in the thrown message when JSON parsing fails, to speed diagnosis.
  4. Handle 401 distinctly (bad/expired key) with the enterApiKey prompt.

Example fix

// before
const body = await res.json().catch(() => null)
const msg = body?.error?.message || body?.message || `${t('keyUsage.queryFailed')} (${res.status})`
throw new Error(msg)

// after
let body: any = null
const raw = await res.text().catch(() => '')
try { body = JSON.parse(raw) } catch {}
const msg = body?.error?.message || body?.message
  || `${t('keyUsage.queryFailed')} (${res.status}${raw ? `: ${raw.slice(0, 120)}` : ''})`
throw new Error(msg)
Defensive patterns

Strategy: try-catch

Validate before calling

const trimmed = apiKey.value.trim();
if (!trimmed || !/^[A-Za-z0-9_-]{20,}$/.test(trimmed)) { showEnterApiKey(); return; } // before fetch

Try / catch

try { await fetchUsage(key); }
catch (e) {
  if (String(e.message).includes('queryFailed')) {
    showError('Gateway unreachable — check API base URL and key'); return;
  }
  showError(e.message);
}

Prevention

When it happens

Trigger: GET /v1/usage returns non-2xx with a non-JSON body: gateway returns HTML error page (502/504 from an nginx in front), plain-text 429 from a rate limiter, connection reset body, or 404 with empty body because the path is wrong (wrong gateway base URL in buildGatewayUrl).

Common situations: Wrong API base URL configured (points at the frontend server which serves the SPA HTML for unknown routes); gateway temporarily down; CDN interposing an HTML challenge page; CORS preflight failure surfacing as an opaque error.

Related errors


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