{"record":{"id":"d0f4a8690a9e39a1","repo":"jackwener/OpenCLI","slug":"boss-redirected-the-job-detail-page-to-the-login-f","errorCode":null,"errorMessage":"BOSS redirected the job detail page to the login flow","messagePattern":"BOSS redirected the job detail page to the login flow","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/boss/detail.js","lineNumber":136,"sourceCode":"async function captureJobDetail(page, jobId) {\n    const url = `https://www.zhipin.com/job_detail/${encodeURIComponent(jobId)}.html`;\n    await navigateTo(page, 'https://www.zhipin.com/web/geek/jobs', 2);\n    for (let navigationAttempt = 0; navigationAttempt < 2; navigationAttempt++) {\n        await navigateTo(page, url, 5);\n        for (let attempt = 0; attempt < 5; attempt++) {\n            try {\n                const domRow = await readRenderedPage(page, jobId);\n                if (domRow?.name && domRow?.description && domRow?.company) return domRow;\n            } catch { /* wait for a complete render */ }\n            if (attempt < 4) await page.wait(1);\n        }\n    }\n    // The retry loop above swallows every read error, so without this check a\n    // session that got bounced to the login wall reports \"incomplete posting\".\n    // The API path this command replaced classified that as AuthRequiredError\n    // via assertOk; keep that signal rather than losing it to the UI rewrite.\n    if (await isLoginWall(page)) {\n        throw new AuthRequiredError(BOSS_DOMAIN, 'BOSS redirected the job detail page to the login flow');\n    }\n    throw new CommandExecutionError('BOSS detail page did not expose a complete job posting');\n}\n\nasync function isLoginWall(page) {\n    try {\n        return await page.evaluate(`\n            (() => {\n                const href = window.location.href || '';\n                if (/\\\\/login|\\\\/user\\\\/login|passport/.test(href)) return true;\n                return !!document.querySelector('.sign-form, .login-card, [class*=\"login-register\"]');\n            })()\n        `) === true;\n    } catch {\n        return false;\n    }\n}\ncli({","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/boss/detail.js#L118-L154","documentation":"captureJobDetail throws AuthRequiredError(BOSS_DOMAIN, 'BOSS redirected the job detail page to the login flow') when, after exhausting all read retries, the page is detected as a login wall (URL contains /login or passport, or a login form element is present). The library deliberately surfaces this as an auth signal — the pre-UI API path classified it the same way via assertOk — so callers don't mistake a logged-out session for a broken job page.","triggerScenarios":"Session cookies expired or were invalidated so navigating to /job_detail/<id>.html redirects to the login flow; BOSS anti-bot measures force a re-login; running without ever completing `opencli` login for www.zhipin.com.","commonSituations":"Expired persistent session after days of inactivity; running headless from a server where the login session lapsed; BOSS risk-control bouncing frequent automated detail fetches to login; cookie strategy not yet established for the domain.","solutions":["Re-authenticate: run the BOSS login flow / `opencli boss login` (or open the browser session) and refresh cookies, then retry the command.","Retry after completing any CAPTCHA/slider verification in the persistent browser session.","Slow down request rate — aggressive sequential detail fetches can trigger risk-control login walls.","Verify the session is alive with a cheap authenticated command before batch-fetching job details.","Check cookies for www.zhipin.com are present and not expired in the profile used by the persistent session."],"exampleFix":"// before\ntry { await detail(securityId) } catch (e) { console.error(e.message) }\n// after\ntry {\n  await detail(securityId);\n} catch (e) {\n  if (isAuthRequiredError(e)) { await bossLogin(); return detail(securityId); }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// cheap authenticated call to verify the session before detail fetches\nconst list = await search({ query: 'test', limit: 1 }); // throws auth errors early if logged out","typeGuard":"function isAuthRequiredError(e) { return e && (e.name === 'AuthRequiredError' || /login flow/i.test(e.message || '')); }","tryCatchPattern":"try {\n  const row = await detail(securityId);\n} catch (e) {\n  if (isAuthRequiredError(e)) {\n    await reloginBoss(); // run the interactive login / refresh cookies\n    return detail(securityId);\n  }\n  throw e;\n}","preventionTips":["Re-login whenever this error appears; it is never a code bug, always session state.","Verify session health with a cheap authenticated command before batch jobs.","Throttle detail fetches — aggressive scraping triggers risk-control login walls.","Use the persistent session profile so cookies survive between runs."],"tags":["auth-required","boss-zhipin","login-wall","session-expired"],"backgroundTag":"login-wall-redirect","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}