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.
Solutions
- Verify buildGatewayUrl('/v1/usage') resolves to the actual gateway origin, not the SPA host.
- curl the URL with the same Bearer key to see the raw non-JSON body and fix the upstream (proxy 502 HTML, etc.).
- Include a snippet of the raw text (first 120 chars) in the thrown message when JSON parsing fails, to speed diagnosis.
- 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
- Verify the gateway base URL before shipping (config check, not a guess)
- Include a raw-body snippet when JSON parsing fails for faster diagnosis
- Handle 401 separately with a re-enter-key prompt
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
- HTTP error! status
- HTTP error! status
- admin.accounts.grok.noResponseBody
- admin.backup.actions.downloadFailed
- auth.dingtalk.callbackMissingToken
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/5083f843519ec1df.
Report an issue: GitHub.
Appendix: 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)