{"record":{"id":"03e229d81db24702","repo":"jackwener/OpenCLI","slug":"boss-search-page-did-not-expose-its-job-list-respo","errorCode":null,"errorMessage":"BOSS search page did not expose its job-list response","messagePattern":"BOSS search page did not expose its job-list response","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/boss/search.js","lineNumber":124,"sourceCode":"        for (const entry of Array.isArray(captures) ? captures : []) {\n            if (!String(entry?.url || '').includes('joblist.json') ||\n                Number(entry?.responseStatus || 0) !== 200 ||\n                typeof entry?.responsePreview !== 'string') {\n                continue;\n            }\n            let payload;\n            try {\n                payload = JSON.parse(entry.responsePreview);\n            } catch {\n                continue;\n            }\n            if (payload && typeof payload === 'object' && 'code' in payload && payload.code !== 0) {\n                assertOk(payload, 'BOSS search failed');\n            }\n            if (Array.isArray(payload?.zpData?.jobList)) return payload;\n        }\n    }\n    throw new CommandExecutionError('BOSS search page did not expose its job-list response');\n}\ncli({\n    site: 'boss',\n    name: 'search',\n    access: 'read',\n    description: 'BOSS直聘搜索职位（不带关键词时返回为你推荐职位）',\n    domain: 'www.zhipin.com',\n    strategy: Strategy.INTERCEPT,\n    navigateBefore: false,\n    browser: true,\n    defaultWindowMode: 'background',\n    siteSession: 'persistent',\n    args: [\n        { name: 'query', positional: true, help: 'Search keyword (optional, empty = recommended jobs)' },\n        { name: 'city', default: '北京', help: 'City name or code (e.g. 杭州, 上海, 101010100)' },\n        { name: 'experience', default: '', help: 'Experience: 在校生(实习)/应届生(校招)/经验不限/1年以内/1-3年/3-5年/5-10年/10年以上' },\n        { name: 'degree', default: '', help: 'Degree: 大专/本科/硕士/博士' },\n        { name: 'salary', default: '', help: 'Salary: 3K以下/3-5K/5-10K/10-15K/15-20K/20-30K/30-50K/50K以上' },","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/boss/search.js#L106-L142","documentation":"captureJobList navigates and reads captured network responses looking for a payload whose zpData.jobList is an array (the BOSS search API response). After checking captured entries — asserting OK on non-zero codes — if no response ever exposed the job list, it throws this CommandExecutionError.","triggerScenarios":"The search page never fired the job-list XHR (bot challenge/captcha, login wall, redirect to a verify page), the response shape changed (no zpData.jobList), or navigation failed so no matching request was captured within the retry attempts.","commonSituations":"BOSS anti-bot verification intercepting the page; recruiter account logged out so the page redirects; BOSS API version change renaming zpData.jobList; heavy throttling causing empty result pages without the standard payload; corporate proxy stripping responses.","solutions":["Open the BOSS site in the automated browser and complete any captcha/login/verification, then retry","Verify the account session is valid and the search page loads manually in that browser profile","Retry after a delay — transient anti-bot throttling often clears","Inspect captured responses (verbose mode) and update the zpData.jobList extraction path if BOSS changed its API shape","Check that the target URL is reachable from the browser (no proxy/firewall blocking)"],"exampleFix":"// before\nawait cli('boss', 'search', { query: 'golang' }); // hits captcha, no jobList captured\n// after: pre-check page state and retry with backoff\nlet jobs;\nfor (let i = 0; i < 3; i++) {\n  try { jobs = await cli('boss', 'search', { query: 'golang' }); break; }\n  catch (e) {\n    if (!String(e.message).includes('did not expose')) throw e;\n    await loginAndVerify(page); // complete captcha / re-login in the browser\n    await sleep(5000 * (i + 1));\n  }\n}","handlingStrategy":"retry","validationCode":"// pre-flight: ensure the session is logged in and the search page renders\nawait page.goto('https://www.zhipin.com/web/chat/index');\nif (await page.$('.nc_iconfont,.verify-wrap')) throw new Error('complete BOSS verification first');","typeGuard":null,"tryCatchPattern":"try {\n  return await cli('boss', 'search', opts);\n} catch (e) {\n  if (String(e.message).includes('did not expose its job-list response')) {\n    await backoff(2); // complete captcha/re-login, then retry\n    return cli('boss', 'search', opts);\n  }\n  throw e;\n}","preventionTips":["Keep the browser profile logged in and verified before batch searches","Throttle request rate to avoid anti-bot interception","Monitor for BOSS API shape changes (zpData.jobList)","Verify searches manually in the same browser profile when failures spike"],"tags":["network-capture","scraping","anti-bot","boss-zhipin"],"backgroundTag":"api-response-shape-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}