{"record":{"id":"6aeb1e3acb066a6f","repo":"jackwener/OpenCLI","slug":"label-request-failed-err-message-err-6aeb1e","errorCode":null,"errorMessage":"${label} request failed: ${err?.message ?? err}","messagePattern":"(.+?) request failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/juejin/utils.js","lineNumber":99,"sourceCode":"/**\n * POST JSON to a Juejin endpoint. The API returns `{ err_no, err_msg, data }`;\n * a non-zero `err_no` is surfaced as a typed `CommandExecutionError`.\n */\nexport async function juejinFetch(path, body, label, method = 'POST') {\n    const url = `${JUEJIN_API_BASE}${path}`;\n    let resp;\n    try {\n        const init = {\n            method,\n            headers: { 'user-agent': UA, accept: 'application/json' },\n        };\n        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}`);","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/juejin/utils.js#L81-L117","documentation":"juejinFetch wraps every outbound `fetch` to api.juejin.cn in a try/catch and rethrows any network-level failure (DNS, TCP, TLS, timeout, abort) as a CommandExecutionError. The original error's message is appended so the caller can see the underlying cause. It means the HTTP request never completed — the server response was never received.","triggerScenarios":"The `fetch(url, init)` call inside juejinFetch rejects: DNS resolution failure for api.juejin.cn, connection refused/timed out, TLS handshake failure, or the request was aborted. Any adapter command (recommend feed, hot list, etc.) that goes through juejinFetch can produce this.","commonSituations":"Running the CLI offline or on a network that blocks api.juejin.cn (common outside China, where Juejin may be slow or firewalled); corporate proxies intercepting TLS; IPv6/DNS misconfiguration; Node fetch agent issues; firewalls dropping the connection mid-request.","solutions":["Verify basic connectivity: `curl -I https://api.juejin.cn` — if this fails, fix DNS/proxy/firewall first.","If behind a corporate proxy, set HTTPS_PROXY / HTTP_PROXY environment variables so Node's fetch can route through it.","Check that the machine has working DNS (`nslookup api.juejin.cn`); add a resolver or hosts entry if needed.","Retry later if the network to Chinese endpoints is degraded (e.g. use a VPN with China egress).","Read the appended `err?.message` in the error text to identify the exact low-level cause (ENOTFOUND, ECONNREFUSED, ECONNRESET, CERT_...)."],"exampleFix":"// before (no connectivity config)\n$ opencli juejin recommend\nCommandExecutionError: juejin recommend request failed: getaddrinfo api.juejin.cn ENOTFOUND\n\n// after (export proxy / fix DNS first)\n$ export HTTPS_PROXY=http://corp-proxy:8080\n$ opencli juejin recommend\nrank 1 ...","handlingStrategy":"try-catch","validationCode":"// pre-flight reachability check\nasync function juejinReachable() {\n  try {\n    const r = await fetch('https://api.juejin.cn', { method: 'HEAD', signal: AbortSignal.timeout(5000) });\n    return true; // any HTTP response means the host is reachable\n  } catch {\n    return false;\n  }\n}\nif (!(await juejinReachable())) throw new Error('api.juejin.cn unreachable; check network/proxy/DNS');","typeGuard":"function isFetchNetworkError(err) {\n  return err instanceof Error && (\n    err.cause != null ||\n    /ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|UND_ERR|CERT/.test(String(err.message))\n  );\n}","tryCatchPattern":"try {\n  const payload = await juejinFetch(path, body, label);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /request failed/.test(err.message)) {\n    console.error(`Network problem reaching Juejin: ${err.message}. Fix connectivity or set HTTPS_PROXY.`);\n    process.exitCode = 2; // transient/network class\n    return;\n  }\n  throw err;\n}","preventionTips":["Run a one-time connectivity probe (HEAD request) before batch jobs and fail fast with a clear message.","Configure HTTPS_PROXY/HTTP_PROXY explicitly in environments behind corporate proxies.","Use AbortSignal.timeout on requests so hangs surface as timeouts instead of stalling scripts.","Monitor DNS + TLS to api.juejin.cn from CI runners; use runners with egress to Chinese endpoints."],"tags":["network","fetch","dns","http-request"],"backgroundTag":"fetch-network-error","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}