{"record":{"id":"2cf06d6e06dcebfa","repo":"jackwener/OpenCLI","slug":"xiaohongshu-collection-page-requires-login","errorCode":null,"errorMessage":"xiaohongshu collection page requires login","messagePattern":"xiaohongshu collection page requires login","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/collection-helpers.js","lineNumber":195,"sourceCode":"`;\n\nasync function throwIfLoginWall(page) {\n    const payload = unwrapBrowserResult(await page.evaluate(LOGIN_WALL_JS));\n    if (payload === true) {\n        throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu collection page requires login; re-login to xiaohongshu.com and retry.');\n    }\n}\n\nexport async function assertOnCollectionProfile(page, userId) {\n    await throwIfLoginWall(page);\n    const payload = unwrapBrowserResult(await page.evaluate(CURRENT_LOCATION_JS));\n    if (!isObject(payload)) {\n        throw new CommandExecutionError('xiaohongshu collection page returned malformed location');\n    }\n    const hostname = toCleanString(payload.hostname).toLowerCase();\n    const pathname = toCleanString(payload.pathname);\n    if (hostname === 'www.xiaohongshu.com' && pathname === '/login') {\n        throw new AuthRequiredError('xiaohongshu collection page requires login');\n    }\n    const expectedPath = `/user/profile/${toCleanString(userId)}`;\n    if (hostname !== 'www.xiaohongshu.com' || pathname !== expectedPath) {\n        throw new CommandExecutionError(`xiaohongshu collection landed on unexpected page: ${toCleanString(payload.href) || `${hostname}${pathname}`}`);\n    }\n}\n\nasync function accumulateInterceptedNotes(page, bucket, fallbackUserId) {\n    const reqs = await page.getInterceptedRequests();\n    if (!Array.isArray(reqs)) {\n        throw new CommandExecutionError('xiaohongshu collection interceptor returned malformed captures');\n    }\n    if (Array.isArray(reqs) && reqs.length > 0)\n        bucket.push(...reqs);\n    return extractNotesFromResponses(bucket, fallbackUserId);\n}\n\nexport async function resolveXhsUserId(page, rawId) {","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/collection-helpers.js#L177-L213","documentation":"assertOnCollectionProfile checks the page URL: if the browser is on www.xiaohongshu.com/login, the library throws AuthRequiredError because the collection fetch was redirected to the login page. It is a second, redirect-based login check complementing throwIfLoginWall.","triggerScenarios":"Navigating to /user/profile/<userId> results in a redirect to /login because the session is unauthenticated or expired.","commonSituations":"Expired cookies triggering a server-side redirect; visiting a private/restricted profile that requires auth; xiaohongshu risk-control redirecting suspicious traffic to login.","solutions":["Re-login to xiaohongshu.com in the automation browser, then retry","Verify the userId is a public profile your session can view","Refresh/persist session cookies before each run","Slow scraping pace to avoid risk-control redirects"],"exampleFix":"// before\nawait page.goto(`/user/profile/${userId}`);\nconst notes = await fetchXhsCollectionNotes(page, userId); // redirected to /login\n// after\nif (page.url().includes('/login')) {\n  await performXhsLogin(page);\n  await page.goto(`/user/profile/${userId}`);\n}\nconst notes = await fetchXhsCollectionNotes(page, userId);","handlingStrategy":"retry","validationCode":"const onLoginPage = (page) => { try { const u = new URL(page.url()); return u.hostname === 'www.xiaohongshu.com' && u.pathname === '/login'; } catch { return false; } };","typeGuard":"const isXhsProfileUrl = (url, userId) => { try { const u = new URL(url); return u.hostname === 'www.xiaohongshu.com' && u.pathname === `/user/profile/${userId}`; } catch { return false; } };","tryCatchPattern":"try { notes = await fetchXhsCollectionNotes(page, userId); } catch (e) { if (e instanceof AuthRequiredError) { await performXhsLogin(page); notes = await fetchXhsCollectionNotes(page, userId); } else throw e; }","preventionTips":["Verify session validity (e.g. load a known authed endpoint) before scraping","Persist and refresh cookies between runs","Treat /login redirects as auth expiry signals and re-auth automatically","Avoid restricted/private profiles your session cannot view"],"tags":["auth","login-redirect","scraping","auth-required-error"],"backgroundTag":"login-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}