{"record":{"id":"7494aa02a18cb892","repo":"jackwener/OpenCLI","slug":"auth-7494aa","errorCode":null,"errorMessage":"auth","messagePattern":"auth","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"critical","filePath":"clis/xueqiu/auth.js","lineNumber":42,"sourceCode":"      if (!res.ok) return { kind: 'http', httpStatus: res.status };\n      const d = await res.json();\n      if (d?.error_code === 60201) {\n        return { kind: 'auth', detail: 'xueqiu portfolio API error_code 60201 用户id无效 — anonymous' };\n      }\n      if (d?.error_code) {\n        return { kind: 'xq-error', errorCode: d.error_code, detail: d.error_description || 'xueqiu API error' };\n      }\n      const uCookie = document.cookie.split('; ').find(c => c.startsWith('u='))?.split('=')[1] || '';\n      const cookiesuCookie = document.cookie.split('; ').find(c => c.startsWith('cookiesu='))?.split('=')[1] || '';\n      if (!uCookie || uCookie === cookiesuCookie) {\n        return { kind: 'auth', detail: 'xueqiu u cookie equals cookiesu (device id) — anonymous despite portfolio API 200' };\n      }\n      return { ok: true, user_id: uCookie };\n    } catch (e) {\n      return { kind: 'exception', detail: String(e && e.message || e) };\n    }\n  })()`);\n  if (probe?.kind === 'auth') throw new AuthRequiredError('xueqiu.com', probe.detail);\n  if (probe?.kind === 'http') throw new CommandExecutionError(`HTTP ${probe.httpStatus} from xueqiu stock API: ${probe.detail || ''}`);\n  if (probe?.kind === 'xq-error') throw new CommandExecutionError(`xueqiu API error_code ${probe.errorCode}: ${probe.detail}`);\n  if (probe?.kind === 'exception') throw new CommandExecutionError(`xueqiu whoami failed: ${probe.detail}`);\n  if (!probe?.ok) throw new CommandExecutionError(`Unexpected xueqiu probe: ${JSON.stringify(probe)}`);\n  return { user_id: String(probe.user_id) };\n}\n\nregisterSiteAuthCommands({\n  site: 'xueqiu',\n  domain: 'xueqiu.com',\n  loginUrl: 'https://xueqiu.com/',\n  columns: ['user_id'],\n  quickCheck: hasXueqiuAccessToken,\n  verify: verifyXueqiuIdentity,\n  poll: async (page) => {\n    if (!await hasXueqiuAccessToken(page)) {\n      throw new AuthRequiredError('xueqiu.com', 'Waiting for Xueqiu xq_a_token cookie');\n    }","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xueqiu/auth.js#L24-L60","documentation":"After the token cookie exists, verifyXueqiuIdentity runs an in-page whoami probe; if the probe reports kind:'auth', it throws AuthRequiredError('xueqiu.com', probe.detail). This means the cookie is present but the server still treats the session as unauthenticated — the token is invalid, expired, or not accepted by the API.","triggerScenarios":"The in-page fetch to xueqiu's whoami/stock API returns an auth-kind rejection despite xq_a_token existing: expired/rotated token, logged-out session elsewhere invalidating the token, missing companion cookies (xq_r_token), or CSRF/origin checks failing.","commonSituations":"Long-lived automation sessions whose token silently expired, logging in on another device that rotated the token, partial cookie sets after a cleared-domain purge, or xueqiu tightening auth requirements.","solutions":["Re-login to xueqiu.com in the automated browser to mint fresh cookies","Confirm companion cookies (e.g. xq_r_token) are also present, not just xq_a_token","Avoid sharing one account across sessions that rotate the token","Retry once after re-auth, then check xueqiu service status if it persists"],"exampleFix":"// before\nif (probe?.kind === 'auth') throw new AuthRequiredError('xueqiu.com', probe.detail);\n// after (user action)\nawait page.login('xueqiu.com'); // refresh expired token, then rerun verifyXueqiuIdentity","handlingStrategy":"retry","validationCode":"const cookies = await page.getCookies({ url: 'https://xueqiu.com' });\nconst hasToken = cookies.some(c => c.name === 'xq_a_token' && c.value);\nif (!hasToken) throw new Error('xq_a_token missing');\n// token presence is not enough; validate via the whoami probe before real calls","typeGuard":null,"tryCatchPattern":"try { await verifyXueqiuIdentity(page); } catch (e) { if (String(e).includes('AuthRequired') || String(e).includes('auth')) { await page.login('xueqiu.com'); return verifyXueqiuIdentity(page); } throw e; }","preventionTips":["Re-login when the whoami probe reports auth failure (token expired)","Keep xq_a_token and xq_r_token cookie pairs intact","Avoid concurrent logins on the same account that rotate tokens","Schedule periodic re-auth for long-running automations"],"tags":["auth","session-expired","xueqiu"],"backgroundTag":"auth-token-expired","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}