{"record":{"id":"21e43cc20c78aca8","repo":"jackwener/OpenCLI","slug":"failed-to-get-user-url-token-from-zhihu","errorCode":null,"errorMessage":"Failed to get user url_token from Zhihu","messagePattern":"Failed to get user url_token from Zhihu","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/zhihu/collections.js","lineNumber":62,"sourceCode":"  domain: 'www.zhihu.com',\n  strategy: Strategy.COOKIE,\n  browser: true,\n  args: [\n    { name: 'limit', type: 'int', default: 20, help: '每页数量（最大 20）' },\n  ],\n  columns: ['rank', 'title', 'item_count', 'description', 'collection_id'],\n  func: async (page, kwargs) => {\n    const { limit = 20 } = kwargs;\n    const requestedLimit = validatePositiveInt(limit, 'limit');\n\n    // 先访问知乎主页建立 session\n    await page.goto('https://www.zhihu.com');\n    // 获取当前用户的 url_token\n    const meData = await fetchJson(page, 'https://www.zhihu.com/api/v4/me?include=url_token', 'Zhihu user info request');\n\n    const urlToken = meData.url_token;\n    if (!urlToken) {\n      throw new CommandExecutionError('Failed to get user url_token from Zhihu', 'Please ensure you are logged in.');\n    }\n\n    const collected = [];\n    const seen = new Set();\n    let totals = 0;\n    let offset = 0;\n    const pageLimit = Math.min(requestedLimit, 20);\n    const maxPages = Math.ceil(requestedLimit / pageLimit) + 2;\n\n    for (let pageIndex = 0; pageIndex < maxPages && collected.length < requestedLimit; pageIndex += 1) {\n      const currentFetchLimit = Math.min(pageLimit, requestedLimit - collected.length);\n      const url = `https://www.zhihu.com/api/v4/people/${urlToken}/collections?include=data%5B*%5D.updated_time&offset=${offset}&limit=${currentFetchLimit}`;\n      const data = await fetchJson(page, url, 'Zhihu favorite collections request');\n      const items = Array.isArray(data.data) ? data.data : [];\n      const paging = data.paging || {};\n      totals = Number(paging.totals || totals || 0);\n\n      for (const item of items) {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zhihu/collections.js#L44-L80","documentation":"The collections command throws CommandExecutionError when the /api/v4/me?include=url_token response contains no url_token. url_token is required to enumerate the current user's collections, so a missing value is treated as a hard failure; the remedy hint points at login state.","triggerScenarios":"The me endpoint returned 200 but with no url_token field — typically because the page is not actually logged in (Zhihu returns an anonymous profile shape), the payload shape changed, or a degraded/blocked response omitted the field.","commonSituations":"Half-logged-in state where the homepage loads but the API session is anonymous; Zhihu A/B payload changes removing/renaming url_token; scraping protection returning sanitized data to suspicious clients.","solutions":["Ensure you are fully logged in to zhihu.com in the CLI browser session, then rerun","Log out and back in to force a fresh authenticated session","Update the CLI in case Zhihu changed the /api/v4/me response shape","Inspect the raw me response with -v and report schema drift if url_token is gone while logged in"],"exampleFix":"// before\nconst urlToken = meData.url_token;\n// after\nconst urlToken = meData?.url_token ?? meData?.data?.url_token; // tolerate shape change, still throw if absent","handlingStrategy":"validation","validationCode":"const me = await fetch('https://www.zhihu.com/api/v4/me?include=url_token', { credentials: 'include' }).then(r => r.json());\nif (!me?.url_token) throw new Error('no url_token: log in to zhihu.com before listing collections');","typeGuard":"function hasUrlToken(me) {\n  return me != null && typeof me === 'object' && typeof me.url_token === 'string' && me.url_token.length > 0;\n}","tryCatchPattern":"try {\n  return await zhihuCollections();\n} catch (e) {\n  if (e.message.includes('url_token')) { await loginToZhihu(); return retryWithBackoff(zhihuCollections, 1); }\n  throw e;\n}","preventionTips":["Verify the me endpoint returns url_token before enumerating collections","Do a full logout/login if the session is in an anonymous half-state","Guard against undefined with optional chaining plus an explicit throw","Update the CLI when Zhihu changes the /api/v4/me payload shape"],"tags":["auth","zhihu","session","schema"],"backgroundTag":"missing-auth-session","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}