{"record":{"id":"4ec6bb1b9f7a4623","repo":"jackwener/OpenCLI","slug":"xianyu-personal-redirected-to-login-finalurl","errorCode":null,"errorMessage":"Xianyu /personal redirected to login: ${finalUrl}","messagePattern":"Xianyu /personal redirected to login: (.+?)","errorType":"error_code","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/xianyu/auth.js","lineNumber":17,"sourceCode":"import { AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';\nimport { registerSiteAuthCommands } from '../_shared/site-auth.js';\n\nasync function hasXianyuIdentityCookie(page) {\n  const cookies = await page.getCookies({ url: 'https://www.goofish.com' });\n  return cookies.some(c => (c.name === 'unb' || c.name === 'tracknick') && c.value);\n}\n\nasync function verifyXianyuIdentity(page) {\n  if (!await hasXianyuIdentityCookie(page)) {\n    throw new AuthRequiredError('goofish.com', 'Xianyu unb/tracknick cookie missing — anonymous');\n  }\n  await page.goto('https://www.goofish.com/personal');\n  await page.wait(2);\n  const finalUrl = await page.evaluate(`location.href`);\n  if (/passport\\.(taobao|goofish)\\.com\\/(member\\/login|login)/.test(String(finalUrl || ''))) {\n    throw new AuthRequiredError('goofish.com', `Xianyu /personal redirected to login: ${finalUrl}`);\n  }\n  const cookies = await page.getCookies({ url: 'https://www.goofish.com' });\n  const tracknick = cookies.find(c => c.name === 'tracknick')?.value || '';\n  const unb = cookies.find(c => c.name === 'unb')?.value || '';\n  const probe = await page.evaluate(`\n    (() => {\n      const bodyText = document.body?.innerText || '';\n      const requiresAuth = /请先登录|登录后/.test(bodyText);\n      const blocked = /验证码|安全验证|异常访问/.test(bodyText);\n      const nick = document.querySelector('.user-name, .user-nick, .nick, [class*=\"nickname\"]')?.innerText?.trim() || '';\n      const html = document.body?.innerHTML || '';\n      const userIdMatch = html.match(/['\"]?userId['\"]?\\\\s*[:=]\\\\s*['\"]?(\\\\d+)/i);\n      return { requiresAuth, blocked, domNick: nick, domUserId: userIdMatch?.[1] || '' };\n    })()\n  `);\n  if (probe.blocked) {\n    throw new AuthRequiredError('goofish.com', 'Xianyu blocked by verification / risk control');\n  }","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xianyu/auth.js#L1-L35","documentation":"After finding identity cookies, verifyXianyuIdentity navigates to https://www.goofish.com/personal and reads the final URL. If the page was redirected to a passport.taobao.com or passport.goofish.com login/member URL, the session cookies are stale or invalid, and an AuthRequiredError is thrown including the redirect target. This catches the case where cookies exist but the server no longer accepts them as a logged-in session.","triggerScenarios":"Navigating to goofish.com/personal with unb/tracknick cookies present but expired or revoked, causing a server-side redirect to a passport login page detected by the regex /passport\\.(taobao|goofish)\\.com\\/(member\\/login|login)/.","commonSituations":"Long-lived browser profile whose Goofish/Taobao session expired; logging out of Taobao/Goofish elsewhere invalidates the token; Taobao rotating session tokens; system clock skew making cookies appear expired.","solutions":["Re-run the xianyu login command and log in again to refresh the session cookies.","Delete stale goofish.com/taobao.com cookies in the CLI's browser profile, then log in fresh.","Verify the system clock is correct (wrong time can invalidate session cookies).","If it recurs quickly, check whether you are logged in on other devices/sessions that may invalidate this one."],"exampleFix":"// before\nawait page.goto('https://www.goofish.com/personal');\n// after\nawait page.goto('https://www.goofish.com/personal');\nconst url = await page.evaluate('location.href');\nif (/passport\\.(taobao|goofish)\\.com/.test(url)) {\n  await clearSiteCookies(page, 'goofish.com');\n  await runInteractiveLogin('xianyu'); // refresh expired session\n}","handlingStrategy":"try-catch","validationCode":"const cookies = await page.getCookies({ url: 'https://www.goofish.com' });\nconst unb = cookies.find(c => c.name === 'unb');\n// presence is necessary but not sufficient — expired sessions still redirect,\n// so probe /personal and inspect the final URL before doing real work\nawait page.goto('https://www.goofish.com/personal');\nconst finalUrl = await page.evaluate('location.href');\nif (/passport\\.(taobao|goofish)\\.com/.test(finalUrl)) await runInteractiveLogin('xianyu');","typeGuard":"function isLoginRedirect(url) {\n  return /passport\\.(taobao|goofish)\\.com\\/(member\\/login|login)/.test(String(url || ''));\n}","tryCatchPattern":"try {\n  await verifyXianyuIdentity(page);\n} catch (err) {\n  if (/redirected to login/.test(err.message)) {\n    await clearSiteCookies(page, 'goofish.com'); // drop stale session\n    await runInteractiveLogin('xianyu');\n  } else throw err;\n}","preventionTips":["Re-login proactively when the session is old instead of waiting for expiry.","Check clock sync (NTP) on machines running the CLI — skewed time breaks cookie validity.","Avoid logging out of Taobao/Goofish in other sessions while the CLI profile is active.","Validate the final URL after navigation before scraping authenticated pages."],"tags":["authentication","session-expired","redirect","goofish"],"backgroundTag":"session-expired-redirect-to-login","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}