jackwener/OpenCLI · error · CliError
FETCH_ERROR
FETCH_ERROR
Error message
HTTP ${resp.status} for ${path} What it means
If the response is JSON, not an auth errcode, but resp.ok is false (any non-2xx other than the 401 auth path already handled), postWebApiWithCookies throws this CliError with code FETCH_ERROR and the HTTP status in the message. It means the WeRead API endpoint itself failed or is unavailable, independent of the caller's arguments.
Source
Thrown at clis/weread/ai-outline.js:49
body: JSON.stringify(body),
});
if (resp.status === 401) {
throw new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first');
}
let data;
try {
data = await resp.json();
} catch {
throw new CliError('PARSE_ERROR', `Invalid JSON response for ${path}`, 'WeRead may have returned an HTML error page');
}
if (data?.errcode === -2010 || data?.errcode === -2012) {
throw new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first');
}
if (!resp.ok) {
throw new CliError('FETCH_ERROR', `HTTP ${resp.status} for ${path}`, 'WeRead API may be temporarily unavailable');
}
return data;
}
async function postWebApi(path, body) {
const url = `${WEB_API}${path}`;
const resp = await fetch(url, {
method: 'POST',
headers: {
'User-Agent': WEREAD_UA,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!resp.ok) {
throw new CliError('FETCH_ERROR', `HTTP ${resp.status} for ${path}`, 'WeRead API may be temporarily unavailable');
}
try {View on GitHub (pinned to 49907e53dc)
Solutions
- Retry with exponential backoff — transient 5xx/502/503 usually resolve within minutes.
- Slow down request rate and add jitter if you saw HTTP 429; respect rate limits.
- Verify the endpoint path is still valid (HTTP 404 suggests WeRead changed its API surface; update the CLI).
- Try from a different network/IP if you get persistent 403 (WAF blocking), and check WeRead's service status.
Example fix
// before
const data = await postWebApiWithCookies(path, body); // dies on HTTP 502
// after
const data = await retry(() => postWebApiWithCookies(path, body), {
retries: 3, backoff: (n) => 2 ** n * 500, retryOn: (e) => e.code === 'FETCH_ERROR'
}); Defensive patterns
Strategy: retry
Try / catch
const backoff = (n) => 2 ** n * 500 + Math.random() * 250;
let data;
for (let attempt = 0; attempt < 4; attempt++) {
try { data = await postWebApiWithCookies(path, body); break; }
catch (e) {
if (e instanceof CliError && e.code === 'FETCH_ERROR' && attempt < 3) {
await new Promise(r => setTimeout(r, backoff(attempt)));
continue;
}
throw e;
}
} Prevention
- Add exponential backoff with jitter around all WeRead API calls.
- Only retry FETCH_ERROR — never retry AUTH_REQUIRED or PARSE_ERROR without action.
- Inspect the status in the message (429 vs 5xx) and tune pacing accordingly.
- Monitor for persistent 404s, which indicate the CLI's endpoint paths need updating after a WeRead site change.
When it happens
Trigger: chapterData hitting the web API and receiving HTTP 5xx (server error), 403 (blocked/WAF), 429 (rate limited), 404 (path changed), or 502/503 (gateway) — any non-ok status after the 401 and errcode checks.
Common situations: WeRead maintenance windows or temporary outages; aggressive scripted polling triggering rate limits; WAF blocking datacenter IPs; API path changes after a WeRead site update.
Related errors
- arXiv API HTTP ${resp.status}
- Bilibili creator comparison request failed: ${detail}
- coingecko categories returned HTTP ${resp.status}
- ${label} returned HTTP ${resp.status}
- Flomo API returned HTTP ${resp.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/18a1932a6a227664.
Report an issue: GitHub.