{"record":{"id":"039650fe1479d8c2","repo":"jackwener/OpenCLI","slug":"label-returned-http-resp-status-039650","errorCode":null,"errorMessage":"${label} returned HTTP ${resp.status}","messagePattern":"(.+?) returned HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/juejin/utils.js","lineNumber":111,"sourceCode":"        if (method === 'POST') {\n            init.headers['content-type'] = 'application/json';\n            init.body = JSON.stringify(body ?? {});\n        }\n        resp = await fetch(url, init);\n    } catch (err) {\n        throw new CommandExecutionError(\n            `${label} request failed: ${err?.message ?? err}`,\n            'Check that api.juejin.cn is reachable from this network.',\n        );\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Juejin throttles bursty traffic; wait a few seconds and retry.',\n        );\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let payload;\n    try {\n        payload = await resp.json();\n    } catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'err_no')) {\n        throw new CommandExecutionError(`${label} returned a malformed API envelope`);\n    }\n    if (payload.err_no !== 0) {\n        throw new CommandExecutionError(`${label} returned err_no ${payload.err_no}: ${payload.err_msg ?? ''}`);\n    }\n    return payload;\n}\n\nexport function readDataArray(payload, label) {\n    if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'data')) {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/juejin/utils.js#L93-L129","documentation":"After the 429 check, juejinFetch requires `resp.ok` (2xx). Any other HTTP status (403, 404, 5xx, etc.) is thrown as a CommandExecutionError carrying the status code. Unlike 429 it has no remediation hint, because the meaning depends entirely on the endpoint and status.","triggerScenarios":"api.juejin.cn returned a non-ok, non-429 status to a juejinFetch call: e.g. 403 from a WAF/anti-bot layer, 404 from a changed/retired endpoint path, 5xx from a Juejin server-side outage, or 301/30x mishandling if the API surface moved.","commonSituations":"Juejin's WAF returning 403 to datacenter/VPN IPs; the adapter's endpoint path breaking after a Juejin API redesign; transient 502/503 during Juejin deploy windows; wrong JUEJIN_API_BASE if someone points it at a mirror.","solutions":["Check the status in the message: 403/405 usually means WAF blocking — try from a residential IP or adjust the user-agent.","For 5xx, treat it as a transient Juejin outage: retry after a minute; check Juejin's status.","For 404, verify the endpoint path in the calling adapter code hasn't been retired by a Juejin API change; update the path.","Confirm JUEJIN_API_BASE is unchanged and correct (https://api.juejin.cn).","Reproduce with `curl -X POST -H 'content-type: application/json' -d '{}' <url>` to see the raw status/body and any WAF challenge page."],"exampleFix":"// before\nthrow new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n\n// after (caller handles transient 5xx with backoff)\ntry {\n  const payload = await juejinFetch('/recommend_api/feed/v1', body, 'juejin recommend');\n} catch (e) {\n  if (/HTTP 5\\d\\d/.test(e.message)) await sleep(5000), retry();\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// optional pre-flight endpoint sanity check\nasync function endpointOk(path) {\n  try {\n    const r = await fetch(`https://api.juejin.cn${path}`, {\n      method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}',\n      signal: AbortSignal.timeout(5000),\n    });\n    return r.status !== 404 && r.status !== 405; // path still exists\n  } catch { return false; }\n}","typeGuard":"function isHttpStatusError(err) {\n  const m = err instanceof CommandExecutionError && err.message.match(/returned HTTP (\\d{3})/);\n  return m ? { isMatch: true, status: Number(m[1]) } : { isMatch: false, status: null };\n}","tryCatchPattern":"try {\n  const payload = await juejinFetch(path, body, label);\n} catch (err) {\n  const { isMatch, status } = isHttpStatusError(err);\n  if (isMatch && status >= 500) {\n    console.error('Juejin server error — retry later.');\n  } else if (isMatch && (status === 403 || status === 405)) {\n    console.error('Blocked or unsupported request — check egress IP / adapter version.');\n  } else {\n    throw err;\n  }\n}","preventionTips":["Classify statuses in your catch block: 5xx retry, 4xx (403/404) investigate config or adapter version.","Pin and periodically update the adapter — Juejin endpoint paths can change without notice.","Reproduce odd statuses with curl against the same URL/headers to see WAF challenge bodies.","Avoid datacenter-only egress IPs for Juejin; they attract WAF 403s."],"tags":["http","http-status","api-endpoint","waf"],"backgroundTag":"unexpected-http-status","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}