{"record":{"id":"8c49fe8565e6f575","repo":"jackwener/OpenCLI","slug":"auth-required-8c49fe","errorCode":"AUTH_REQUIRED","errorMessage":"AUTH_REQUIRED: Not logged in to WeRead","messagePattern":"AUTH_REQUIRED: Not logged in to WeRead","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/weread/utils.js","lineNumber":171,"sourceCode":"                'User-Agent': WEREAD_UA,\n                'Origin': 'https://weread.qq.com',\n                'Referer': 'https://weread.qq.com/',\n                ...(cookieHeader ? { 'Cookie': cookieHeader } : {}),\n            },\n        });\n    }\n    catch (error) {\n        throw new CliError('FETCH_ERROR', `Failed to fetch ${path}: ${error instanceof Error ? error.message : String(error)}`, 'WeRead API may be temporarily unavailable');\n    }\n    let data;\n    try {\n        data = await resp.json();\n    }\n    catch {\n        throw new CliError('PARSE_ERROR', `Invalid JSON response for ${path}`, 'WeRead may have returned an HTML error page');\n    }\n    if (isAuthErrorResponse(resp, data)) {\n        throw new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first');\n    }\n    if (!resp.ok) {\n        throw new CliError('FETCH_ERROR', `HTTP ${resp.status} for ${path}`, 'WeRead API may be temporarily unavailable');\n    }\n    if (data?.errcode != null && data.errcode !== 0) {\n        throw new CliError('API_ERROR', data.errmsg ?? `WeRead API error ${data.errcode}`);\n    }\n    return data;\n}\nfunction getUniqueRawBookIds(snapshot) {\n    return Array.from(new Set(snapshot.rawBooks\n        .map((book) => String(book?.bookId || '').trim())\n        .filter(Boolean)));\n}\n/** Mirror of hasTrustedIndexes in buildShelfSnapshotPollScript — keep in sync */\nfunction getTrustedIndexedBookIds(snapshot) {\n    const rawBookIds = getUniqueRawBookIds(snapshot);\n    if (rawBookIds.length === 0)","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread/utils.js#L153-L189","documentation":"Thrown by fetchPrivateApi when the private WeRead API indicates the session is not authenticated: either an HTTP 401 status or a JSON body whose errcode is in {-2010, -2012} (WeRead's auth error codes), detected by isAuthErrorResponse. Thrown as CliError code AUTH_REQUIRED with the remediation hint to log in to weread.qq.com in Chrome first, because cookies for the request are extracted from the browser profile.","triggerScenarios":"Any fetchPrivateApi call where resp.status === 401 or Number(data.errcode) is -2010/-2012: expired wr_sso/wr_vid session cookies, a Chrome profile that was never logged in, cookies cleared by browser hygiene settings, or the user logging out in Chrome since the last run.","commonSituations":"WeRead session expiry (days after last browser login); switching Chrome profiles so the CLI reads cookies from a non-logged-in profile; clearing cookies/site data; cookie extraction from the wrong profile directory; using the CLI before ever logging in on that machine.","solutions":["Open https://weread.qq.com in Chrome, log in (scan QR with the WeChat/WeRead app), then rerun the CLI — this refreshes the extracted cookies.","Verify the cookies being sent: check that wr_vid and wr_sso exist via page.getCookies({ domain: 'weread.qq.com' }).","Confirm the CLI is pointed at the Chrome profile you actually logged in with (not a work/personal profile switch).","Catch CliError code 'AUTH_REQUIRED' in scripts and pause/notify the user instead of retrying — retrying cannot fix an expired session.","If sessions expire frequently, keep the Chrome tab logged in / disable 'clear cookies on exit' settings."],"exampleFix":"// before\nconst highlights = await fetchPrivateApi(page, '/u/book/notes', params);\n// after\ntry {\n  const highlights = await fetchPrivateApi(page, '/u/book/notes', params);\n} catch (e) {\n  if (e.code === 'AUTH_REQUIRED') {\n    console.error('Please log in to https://weread.qq.com in Chrome, then retry.');\n    process.exitCode = 2;\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Check auth cookies exist in the browser profile before private-API calls\nconst cookies = await page.getCookies({ domain: 'weread.qq.com' });\nconst loggedIn = ['wr_vid', 'wr_sso'].every(n => cookies.some(c => c.name === n));\nif (!loggedIn) console.warn('Not logged in to WeRead — AUTH_REQUIRED expected; log in at https://weread.qq.com in Chrome');","typeGuard":"function isAuthRequiredError(e) {\n  return e != null && typeof e === 'object' && e.code === 'AUTH_REQUIRED';\n}","tryCatchPattern":"import { CliError } from '@jackwener/opencli/errors';\ntry {\n  const data = await callWereadPrivateCommand();\n} catch (e) {\n  if (isAuthRequiredError(e)) {\n    console.error('Not logged in to WeRead. Log in to https://weread.qq.com in Chrome, then rerun.');\n    process.exitCode = 2; // do NOT retry — the session cannot self-heal\n    return;\n  }\n  throw e;\n}","preventionTips":["Verify wr_vid and wr_sso cookies exist before invoking private-API commands","Log in freshly in Chrome whenever AUTH_REQUIRED appears; never retry blindly","Ensure the CLI reads cookies from the Chrome profile you actually log in with","Disable cookie-clearing on browser exit for weread.qq.com to reduce session loss","In scripts, exit with a distinct code on AUTH_REQUIRED so pipelines can prompt the user"],"tags":["auth","cookies","weread","login","session-expired"],"backgroundTag":"auth-required-session-expired","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}