{"record":{"id":"5615b7d543b3e1fc","repo":"jackwener/OpenCLI","slug":"xueqiu-com-5615b7","errorCode":null,"errorMessage":"xueqiu.com","messagePattern":"xueqiu\\.com","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/xueqiu/watchlist.js","lineNumber":26,"sourceCode":"    description: '获取雪球自选股/模拟组合股票列表',\n    domain: 'xueqiu.com',\n    browser: true,\n    args: [\n        {\n            name: 'pid',\n            default: '-1',\n            help: '分组ID：-1=全部(默认) -4=模拟 -5=沪深 -6=美股 -7=港股 -10=实盘 0=持仓（通过 xueqiu groups 获取）',\n        },\n        { name: 'limit', type: 'int', default: 100, help: '默认 100' },\n    ],\n    columns: ['symbol', 'name', 'price', 'changePercent'],\n    func: async (page, kwargs) => {\n        await page.goto('https://xueqiu.com');\n        const pid = String(kwargs.pid || '-1');\n        const url = `https://stock.xueqiu.com/v5/stock/portfolio/stock/list.json?size=100&category=1&pid=${encodeURIComponent(pid)}`;\n        const d = await fetchXueqiuJson(page, url);\n        if (!d.data?.stocks)\n            throw new AuthRequiredError('xueqiu.com');\n        return (d.data.stocks || []).slice(0, kwargs.limit).map((s) => ({\n            symbol: s.symbol,\n            name: s.name,\n            price: s.current,\n            change: s.chg,\n            changePercent: s.percent != null ? s.percent.toFixed(2) + '%' : null,\n            volume: s.volume,\n            url: 'https://xueqiu.com/S/' + s.symbol,\n        }));\n    },\n});\n","sourceCodeStart":8,"sourceCodeEnd":38,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xueqiu/watchlist.js#L8-L38","documentation":"The xueqiu watchlist command calls the portfolio stock list API and expects d.data.stocks to exist. When the response is JSON but has no data.stocks field, it throws AuthRequiredError('xueqiu.com') because xueqiu typically returns an empty/error envelope ({error_code:..., error_description:...}) instead of portfolio data when the session is not authenticated to view that portfolio. The library treats a missing stocks array as an auth problem rather than an empty list.","triggerScenarios":"Calling `xueqiu watchlist` (any pid) when the response envelope lacks data.stocks — session not logged in, the pid does not exist or belongs to another user, or error_code != 0 in the response body.","commonSituations":"Running with an expired session where the API returns 200 with an error envelope; typo'd or invalid pid group id; private portfolio not accessible to the logged-in account.","solutions":["Log into xueqiu.com in the CLI browser session, then re-run the command","Run `xueqiu groups` to list valid pid values and use one of them","Retry with the default pid (-1) to test whether the issue is pid-specific","Inspect the raw API response to confirm error_code/error_description"],"exampleFix":"// before\nconst d = await fetchXueqiuJson(page, url);\nif (!d.data?.stocks) throw new AuthRequiredError('xueqiu.com');\n// after (surface the API's own error message)\nconst d = await fetchXueqiuJson(page, url);\nif (d.error_code) throw new Error(`xueqiu error ${d.error_code}: ${d.error_description}`);\nif (!d.data?.stocks) throw new AuthRequiredError('xueqiu.com');","handlingStrategy":"validation","validationCode":"const loggedIn = await page.evaluate(() => document.cookie.includes('xq_a_token'));\nif (!loggedIn) throw new Error('Login to xueqiu.com before fetching watchlist');","typeGuard":"function hasStocks(d) { return d != null && d.data != null && Array.isArray(d.data.stocks); }","tryCatchPattern":"try {\n  const rows = await watchlist({ pid });\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    return { error: 'AUTH_REQUIRED', hint: 'Log into xueqiu.com, or verify pid via xueqiu groups' };\n  }\n  throw e;\n}","preventionTips":["Ensure the browser session is logged into xueqiu.com before running watchlist","Use valid pid values obtained from `xueqiu groups` (e.g. -1, -4, -5, -6, -7, -10, 0)","Check the API envelope's error_code field to distinguish auth issues from bad pid","Fall back to the default pid (-1) when a specific pid returns no data"],"tags":["auth","xueqiu","empty-response","session"],"backgroundTag":"auth-session-expired","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}