{"record":{"id":"9a8f2e870ff4bcee","repo":"jackwener/OpenCLI","slug":"juejin-recommend-returned-has-more-without-a-next","errorCode":null,"errorMessage":"juejin recommend returned has_more without a next cursor","messagePattern":"juejin recommend returned has_more without a next cursor","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/juejin/recommend.js","lineNumber":53,"sourceCode":"        { name: 'cursor', type: 'string', default: '0', help: 'Pagination cursor; pass back the previous response\\'s next-page cursor to keep scrolling.' },\n    ],\n    columns: ['rank', 'article_id', 'title', 'brief', 'views', 'likes', 'comments', 'author', 'tags', 'url', 'next_cursor', 'has_more'],\n    func: async (args) => {\n        const limit = requireBoundedInt(args.limit, 20, 100);\n        const cursor = requireCursor(args.cursor);\n        const payload = await juejinFetch(\n            '/recommend_api/v1/article/recommend_all_feed',\n            { id_type: 2, client_type: 2608, sort_type: 200, limit, cursor },\n            'juejin recommend',\n        );\n        const data = readDataArray(payload, 'juejin recommend');\n        const nextCursor = readResponseCursor(payload.cursor);\n        if (payload.has_more != null && typeof payload.has_more !== 'boolean') {\n            throw new CommandExecutionError('juejin recommend returned a malformed has_more flag');\n        }\n        const hasMore = payload.has_more == null ? '' : String(payload.has_more);\n        if (payload.has_more === true && !nextCursor) {\n            throw new CommandExecutionError('juejin recommend returned has_more without a next cursor');\n        }\n        return data.slice(0, limit).map((row, i) => ({\n            ...mapFeedItem(row, i + 1),\n            next_cursor: nextCursor,\n            has_more: hasMore,\n        }));\n    },\n});\n","sourceCodeStart":35,"sourceCodeEnd":62,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/juejin/recommend.js#L35-L62","documentation":"When the Juejin recommend API says has_more is true (more pages exist) but provides no usable next cursor, the CLI throws a CommandExecutionError because continuing pagination would be impossible. This is an internal-consistency check on the API response: the two fields must agree.","triggerScenarios":"A recommend response where payload.has_more === true while payload.cursor is empty, null, undefined, or '' (readResponseCursor returns '' for those).","commonSituations":"Juejin backend bug where the final-but-nonempty page still sets has_more true without a cursor; hitting a rate-limited or degraded API variant that truncates fields; stale cached responses with missing cursor fields.","solutions":["Retry the request; if the API consistently omits the cursor, treat it as an upstream bug.","Work around by keeping the last known cursor or by raising --limit so fewer pages are needed.","Check whether the API changed pagination semantics and update the CLI accordingly.","Report to the Juejin/CLI maintainers with the raw response payload."],"exampleFix":"// before (hard failure on inconsistency)\nif (payload.has_more === true && !nextCursor) {\n    throw new CommandExecutionError('juejin recommend returned has_more without a next cursor');\n}\n// after (degrade gracefully)\nconst hasMore = payload.has_more === true && nextCursor;","handlingStrategy":"fallback","validationCode":"if (payload.has_more === true && !(payload.cursor != null && payload.cursor !== '')) { /* inconsistent page — stop paginating */ }","typeGuard":"function canContinue(p){ const c = p.cursor; return p.has_more === true && c != null && c !== ''; }","tryCatchPattern":"try {\n  return cliRecommend({ limit, cursor });\n} catch (e) {\n  if (/has_more without a next cursor/.test(e.message)) return { items: lastPage.items, has_more: false };\n  throw e;\n}","preventionTips":["Treat (has_more && next_cursor) as the real loop condition, not has_more alone.","Cap pagination iterations defensively to avoid infinite loops on buggy API states.","Log the raw payload when this fires and report the upstream inconsistency to maintainers."],"tags":["pagination","cursor","api-response","cli"],"backgroundTag":"malformed-pagination-cursor","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}