{"record":{"id":"effaf99c3089a3d2","repo":"jackwener/OpenCLI","slug":"label-returned-err-no-payload-err-no-payl","errorCode":null,"errorMessage":"${label} returned err_no ${payload.err_no}: ${payload.err_msg ?? ''}","messagePattern":"(.+?) returned err_no (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/juejin/utils.js","lineNumber":123,"sourceCode":"        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')) {\n        throw new CommandExecutionError(`${label} returned a malformed payload`);\n    }\n    if (!Array.isArray(payload.data)) {\n        throw new CommandExecutionError(`${label} returned a non-array data field`);\n    }\n    if (payload.data.length === 0) {\n        throw new EmptyResultError(label, `${label} returned no articles.`);\n    }\n    return payload.data;\n}\n\nfunction readArticleId(value, label) {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/juejin/utils.js#L105-L141","documentation":"juejinFetch in clis/juejin/utils.js:123 validates the Juejin API envelope `{ err_no, err_msg, data }`. When the HTTP response is valid JSON with a proper envelope but `err_no` is non-zero, the library surfaces the API-level business error as a CommandExecutionError including the numeric code and err_msg. This is the Juejin server rejecting the request (bad parameters, restricted content, etc.), not a transport failure.","triggerScenarios":"Any call path through juejinFetch (e.g. recommend feed, hot list, category article queries) where api.juejin.cn responds HTTP 200 with a JSON body whose err_no !== 0, such as an invalid cursor, unknown category id, deleted/private article id, or endpoint parameter the API refuses.","commonSituations":"Querying a category with a raw id that no longer exists; passing a cursor beyond the end of the feed; requesting an article that was deleted or made private; Juejin changing the endpoint contract so previously valid params now return an error code; hitting internal API endpoints that now require auth.","solutions":["Read the err_msg in the error message — it states the API's own reason; fix the request parameter it points to (cursor, category id, article id).","Verify the category id or slug is one of the supported aliases in CATEGORY_ALIASES (backend, frontend, android, ios, ai) or a currently valid numeric id.","Reset the cursor to '0' or omit it to restart pagination from the beginning of the feed.","Check whether the target article still exists by opening https://juejin.cn/post/<id> in a browser; skip deleted/private items.","Retry after a short delay in case the code indicates a transient server-side condition; if persistent, report the changed API contract upstream."],"exampleFix":"// before: cursor past end of feed\nconst items = await listArticles({ cursor: '9999999' });\n// after: start from the default cursor and page forward\nconst items = await listArticles({ cursor: '0' });","handlingStrategy":"try-catch","validationCode":"// Cannot pre-validate server-side err_no; validate inputs you control\ncursor = requireCursor(rawCursor);          // non-negative decimal integer\ncategoryId = resolveCategory(rawCategory);  // known alias or 16-20 digit id","typeGuard":"function isOkEnvelope(p) {\n  return p !== null && typeof p === 'object' && !Array.isArray(p)\n    && Object.hasOwn(p, 'err_no') && p.err_no === 0;\n}","tryCatchPattern":"try {\n  const payload = await juejinFetch(path, body, 'juejin list');\n  // ...\n} catch (err) {\n  if (err instanceof CommandExecutionError && /err_no \\d+/.test(err.message)) {\n    const code = Number(err.message.match(/err_no (\\d+)/)?.[1]);\n    console.error(`Juejin API rejected the request (err_no=${code}); check your cursor/category/article id.`);\n    return;\n  }\n  throw err;\n}","preventionTips":["Always pass cursors through requireCursor and categories through resolveCategory before calling the API.","Verify article/category ids still resolve by loading the juejin.cn URL before relying on them.","Retry once on transient-looking codes; log err_no values you cannot explain and report contract changes.","Keep the adapter's endpoint paths in sync with the live Juejin API."],"tags":["api","http","response-envelope","juejin"],"backgroundTag":"api-error-code-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}