jackwener/OpenCLI · error · CommandExecutionError

${label} request failed: HTTP ${resp.status}

Error message

${label} request failed: HTTP ${resp.status}

What it means

After the fetch succeeds, fetchJson checks resp.ok; any non-2xx HTTP status (403, 404, 429, 5xx) throws CommandExecutionError '<label> request failed: HTTP <status>'. The server answered but rejected or failed the request.

Source

Thrown at clis/weread/book-search.js:88

    }
    if (pathParts.length !== 3) {
        return '';
    }
    return url.toString();
}

async function fetchJson(url, label) {
    let resp;
    try {
        resp = await fetch(url.toString(), {
            headers: { 'User-Agent': WEREAD_UA },
        });
    }
    catch (error) {
        throw new CommandExecutionError(`${label} request failed: ${error instanceof Error ? error.message : String(error)}`);
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} request failed: HTTP ${resp.status}`);
    }
    try {
        return await resp.json();
    }
    catch {
        throw new CommandExecutionError(`${label} returned invalid JSON`);
    }
}

async function fetchText(url, label) {
    let resp;
    try {
        resp = await fetch(url.toString(), {
            headers: { 'User-Agent': WEREAD_UA },
        });
    }
    catch (error) {
        throw new CommandExecutionError(`${label} request failed: ${error instanceof Error ? error.message : String(error)}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the status code in the message: 403/429 → slow down or wait; 404 → endpoint changed; 5xx → retry later
  2. Add exponential backoff on 429/5xx responses
  3. Reduce request frequency or batch size
  4. Update the CLI if WeRead changed the endpoint path
  5. Check with curl -I using the same User-Agent to reproduce outside the tool

Example fix

// before (no status handling)
const data = await fetchJson(url, 'WeRead book search');
// after (backoff on 429/5xx)
try {
  const data = await fetchJson(url, 'WeRead book search');
} catch (e) {
  if (/HTTP (429|5\d\d)/.test(e.message)) { await sleep(2000); /* retry */ }
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// Optional pre-flight status check
const head = await fetch(url, { method: 'HEAD', headers: { 'User-Agent': WEREAD_UA } });
if (head.status === 429) await sleep(2000); // back off before the real request

Type guard

null

Try / catch

try {
  const data = await fetchJson(url, 'WeRead book search');
} catch (e) {
  const m = e.message.match(/HTTP (\d{3})/);
  if (m) {
    const status = Number(m[1]);
    if (status === 429 || status >= 500) { /* retry with exponential backoff */ }
    else if (status === 403) { console.error('Blocked/rate-limited; slow down or authenticate.'); }
  }
  throw e;
}

Prevention

When it happens

Trigger: WeRead returns 403 (anti-bot/rate limiting or blocked User-Agent), 404 (endpoint moved), 429 (too many requests), or 5xx (server outage) for the /web/search/global call.

Common situations: Aggressive scripted querying triggers rate limiting, the API surface changes or is deprecated, regional blocking, or WeRead's WAF flags the CLI's User-Agent.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/99d5024cb1946f56. Report an issue: GitHub.