{"record":{"id":"2b793701ca88b845","repo":"jackwener/OpenCLI","slug":"boss-recommend-response-did-not-include-zpdata-fri","errorCode":null,"errorMessage":"Boss recommend response did not include zpData.friendList","messagePattern":"Boss recommend response did not include zpData\\.friendList","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/boss/utils.js","lineNumber":174,"sourceCode":"    const jobId = opts.jobId ?? '0';\n    const url = `https://${BOSS_DOMAIN}/wapi/zprelation/friend/getBossFriendListV2.json?page=${pageNum}&status=0&jobId=${jobId}`;\n    const data = await bossFetch(page, url, { allowNonZero: opts.allowNonZero });\n    if (opts.allowNonZero && data.code !== 0) return data;\n    const list = data.zpData?.friendList;\n    if (!Array.isArray(list)) {\n        throw new CommandExecutionError('Boss friend list response did not include zpData.friendList');\n    }\n    return list;\n}\n/**\n * Fetch the recommended candidates (greetRecSortList).\n */\nexport async function fetchRecommendList(page) {\n    const url = `https://${BOSS_DOMAIN}/wapi/zprelation/friend/greetRecSortList`;\n    const data = await bossFetch(page, url);\n    const list = data.zpData?.friendList;\n    if (!Array.isArray(list)) {\n        throw new CommandExecutionError('Boss recommend response did not include zpData.friendList');\n    }\n    return list;\n}\n/**\n * Find a friend by encryptUid, searching through friend list and optionally greet list.\n * Returns null if not found.\n */\nexport async function findFriendByUid(page, encryptUid, opts = {}) {\n    const maxPages = opts.maxPages ?? 1;\n    const checkGreetList = opts.checkGreetList ?? false;\n    // Search friend list pages\n    for (let p = 1; p <= maxPages; p++) {\n        const result = await fetchFriendList(page, { pageNum: p, allowNonZero: opts.allowNonZero });\n        if (opts.allowNonZero && !Array.isArray(result)) {\n            return { friend: null, code: result.code };\n        }\n        const friends = Array.isArray(result) ? result : [];\n        const found = friends.find((f) => f.encryptUid === encryptUid);","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/boss/utils.js#L156-L192","documentation":"fetchRecommendList calls Boss直聘's greetRecSortList API and expects data.zpData.friendList to be an array of recommended candidates. When the response lacks that field or it is not an array (login expiry, anti-bot gate, API schema change, or code!=0 response shape), the library throws this CommandExecutionError instead of returning a bogus value.","triggerScenarios":"Calling candidates, friends, or greetList commands when the greetRecSortList endpoint returns a payload without zpData.friendList — e.g. session cookie expired, page not logged in, rate-limited/blocked request, or Boss changed the response schema.","commonSituations":"Boss session expired after idle; account flagged and WAPI returns an error envelope; scraping without a logged-in Playwright page; upstream API refactor renamed friendList.","solutions":["Re-login to Boss直聘 in the automation page and retry the command","Log the full data object (data.code, data.zpData keys) at the throw site to confirm whether it is an auth/rate-limit envelope vs schema change","Verify you are passing a logged-in page to fetchRecommendList (bossFetch uses that page's cookies)","Pin/update the scraper version if Boss changed the API schema and update the field path"],"exampleFix":"// before\nconst data = await bossFetch(page, url);\nconst list = data.zpData?.friendList;\n// after\nconst data = await bossFetch(page, url);\nif (data.code !== 0) throw new AuthRequiredError(BOSS_DOMAIN, `Boss API code=${data.code}`);\nconst list = data.zpData?.friendList;","handlingStrategy":"type-guard","validationCode":"const cookies = await page.getCookies({ url: `https://${BOSS_DOMAIN}` });\nif (!cookies.some(c => c.value)) throw new Error('Not logged in to Boss — login first');","typeGuard":"function hasFriendList(d) { return Array.isArray(d?.zpData?.friendList); }","tryCatchPattern":"try { const list = await fetchRecommendList(page); }\ncatch (e) { if (e instanceof CommandExecutionError) { await relogin(page); return fetchRecommendList(page); } throw e; }","preventionTips":["Persist and restore login cookies (storageState) between runs","Check data.code before reading zpData fields","Log the raw response when the shape guard fails to detect schema changes early","Retry once after re-login before surfacing the error"],"tags":["boss-zhipin","api-response","missing-field","auth"],"backgroundTag":"api-response-missing-field","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}