{"record":{"id":"99d5024cb1946f56","repo":"jackwener/OpenCLI","slug":"label-request-failed-http-resp-status","errorCode":null,"errorMessage":"${label} request failed: HTTP ${resp.status}","messagePattern":"(.+?) request failed: HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weread/book-search.js","lineNumber":88,"sourceCode":"    }\n    if (pathParts.length !== 3) {\n        return '';\n    }\n    return url.toString();\n}\n\nasync function fetchJson(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url.toString(), {\n            headers: { 'User-Agent': WEREAD_UA },\n        });\n    }\n    catch (error) {\n        throw new CommandExecutionError(`${label} request failed: ${error instanceof Error ? error.message : String(error)}`);\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} request failed: HTTP ${resp.status}`);\n    }\n    try {\n        return await resp.json();\n    }\n    catch {\n        throw new CommandExecutionError(`${label} returned invalid JSON`);\n    }\n}\n\nasync function fetchText(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url.toString(), {\n            headers: { 'User-Agent': WEREAD_UA },\n        });\n    }\n    catch (error) {\n        throw new CommandExecutionError(`${label} request failed: ${error instanceof Error ? error.message : String(error)}`);","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/book-search.js#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the status code in the message: 403/429 → slow down or wait; 404 → endpoint changed; 5xx → retry later","Add exponential backoff on 429/5xx responses","Reduce request frequency or batch size","Update the CLI if WeRead changed the endpoint path","Check with curl -I using the same User-Agent to reproduce outside the tool"],"exampleFix":"// before (no status handling)\nconst data = await fetchJson(url, 'WeRead book search');\n// after (backoff on 429/5xx)\ntry {\n  const data = await fetchJson(url, 'WeRead book search');\n} catch (e) {\n  if (/HTTP (429|5\\d\\d)/.test(e.message)) { await sleep(2000); /* retry */ }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// Optional pre-flight status check\nconst head = await fetch(url, { method: 'HEAD', headers: { 'User-Agent': WEREAD_UA } });\nif (head.status === 429) await sleep(2000); // back off before the real request","typeGuard":"null","tryCatchPattern":"try {\n  const data = await fetchJson(url, 'WeRead book search');\n} catch (e) {\n  const m = e.message.match(/HTTP (\\d{3})/);\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 429 || status >= 500) { /* retry with exponential backoff */ }\n    else if (status === 403) { console.error('Blocked/rate-limited; slow down or authenticate.'); }\n  }\n  throw e;\n}","preventionTips":["Rate-limit your own requests (throttle/queue) to avoid 429s","Use exponential backoff on 429 and 5xx responses","Pin and update the CLI when WeRead changes endpoints (watch for 404s)","Test the endpoint with curl -I using the same User-Agent when diagnosing"],"tags":["network","http-status","rate-limiting"],"backgroundTag":"http-error-status","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}