CherryHQ/cherry-studio · error · Error
QQ API request failed ${endpoint}: HTTP ${response.status} -
Error message
QQ API request failed ${endpoint}: HTTP ${response.status} - ${errorText} What it means
Generic gateway thrown by QqAdapter.apiRequest() after net.fetch to a QQ API endpoint returns non-2xx. Embeds endpoint, status, and the raw errorText (with a .catch fallback to ''). Used by gateway lookup, message send (sendToChat), and other QQ REST calls.
Source
Thrown at src/main/ai/channels/adapters/qq/QqAdapter.ts:205
private async apiRequest(
endpoint: string,
options?: { method?: string; body?: Record<string, unknown> }
): Promise<Response> {
const token = await this.getAccessToken()
const response = await net.fetch(endpoint, {
method: options?.method ?? 'GET',
headers: {
Authorization: `QQBot ${token}`,
'Content-Type': 'application/json',
'X-Union-Appid': this.appId
},
...(options?.body ? { body: JSON.stringify(options.body) } : {})
})
if (!response.ok) {
const errorText = await response.text().catch(() => '')
throw new Error(`QQ API request failed ${endpoint}: HTTP ${response.status} - ${errorText}`)
}
return response
}
private async getGatewayUrl(): Promise<string> {
const response = await this.apiRequest(`${QQ_API_BASE}/gateway`)
const data = (await response.json()) as { url: string }
return data.url
}
private async startGateway(): Promise<void> {
if (this.isConnecting || this.shouldStop) return
this.isConnecting = true
try {
this.cleanup()
View on GitHub (pinned to 726446b54c)
Solutions
- On 401, invalidate this.tokenCache and call fetchAccessToken() once, then retry the original request (the cache has expiresAt but a server-side revocation can preempt it).
- Read the embedded errorText for QQ's {code, message,...} to find the specific cause (e.g. permission scope missing).
- For 429, honor Retry-After / back off.
- Verify X-Union-Appid equals this.appId and the endpoint path matches the chat type.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-send: ensure the token cache is fresh. if (!qq.tokenFresh()) await qq.refreshToken() await qq.sendToChat(chatId, text)
Type guard
function isQqApiHttpError(e: unknown): e is Error {
return e instanceof Error && /^QQ API request failed .*: HTTP \d+/.test(e.message)
} Try / catch
try {
await qq.sendToChat(chatId, text)
} catch (e) {
if (isQqApiHttpError(e)) {
const status = Number(/HTTP (\d+)/.exec(e.message)?.[1] ?? 0)
if (status === 401) { await qq.invalidateTokenAndRefresh(); return qq.sendToChat(chatId, text) }
if (status === 429 || status >= 500) return backoffAndRetry()
}
throw e
} Prevention
- On 401, invalidate the token cache and refresh once, then retry.
- Honor Retry-After/back off on 429 and 5xx.
- Verify X-Union-Appid and endpoint path match the chat type.
When it happens
Trigger: apiRequest() (line ~188) fetches an endpoint with Authorization: QQBot <token> and X-Union-Appid; response.ok is false; errorText is read and the message is thrown. Hit during getGatewayUrl, message POSTs, etc.
Common situations: Access token expired (QQ returns 401 — token cache TTL missed), insufficient bot permissions for the target user/group/channel, rate limited, or hitting an endpoint with the wrong X-Union-Appid.
Related errors
- Discord API error ${url}: HTTP ${response.status} - ${errorT
- Failed to get access token: HTTP ${response.status}
- Failed to get gateway URL: HTTP ${response.status} - ${error
- Invalid token response from QQ API: ${errorText}
- Brave API error: ${response.status} ${response.statusText}\n
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/2ee7f2f72052ac91.
Report an issue: GitHub.