{"record":{"id":"b4162c4ffa9d0f46","repo":"jackwener/OpenCLI","slug":"auth-required-cannot-resolve-viewer-secuid-login","errorCode":null,"errorMessage":"AUTH_REQUIRED: cannot resolve viewer secUid (login required)","messagePattern":"AUTH_REQUIRED: cannot resolve viewer secUid \\(login required\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/tiktok/following.js","lineNumber":77,"sourceCode":"      return false;\n    });\n    return found;\n  }\n\n  const universal = findUniversalData();\n  let viewerSecUid = findViewerSecUid(universal);\n  const msToken = getCookie('msToken');\n\n  if (!viewerSecUid) {\n    try {\n      const me = await fetchJson('/api/user/info/?aid=' + aid + (msToken ? '&msToken=' + encodeURIComponent(msToken) : ''));\n      viewerSecUid = String(me?.userInfo?.user?.secUid || me?.user?.secUid || me?.userInfo?.secUid || '').trim();\n    } catch {\n      // Fall through to typed AUTH_REQUIRED below.\n    }\n  }\n  if (!viewerSecUid) {\n    throw new Error('AUTH_REQUIRED: cannot resolve viewer secUid (login required)');\n  }\n\n  const dedup = new Map();\n  let apiFailure = null;\n  let cursor = 0;\n  for (let page = 0; page < maxPages && dedup.size < limit; page += 1) {\n    const params = new URLSearchParams({\n      aid,\n      scene: '21',\n      secUid: viewerSecUid,\n      count: String(pageSize),\n      minCursor: String(cursor),\n      maxCursor: '0',\n    });\n    if (msToken) params.set('msToken', msToken);\n    try {\n      const data = await fetchJson('/api/user/list/?' + params.toString());\n      assertTikTokApiSuccess(data, 'user-list');","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/following.js#L59-L95","documentation":"The following-list command must know the logged-in viewer's secUid before it can page /api/user/list. It first scans the page's __UNIVERSAL_DATA_FOR_REHYDRATION__ snapshot for the owner's secUid, then falls back to fetching /api/user/info. If both yield nothing, the session is not a usable logged-in session, so the library throws AUTH_REQUIRED instead of querying with a blank secUid.","triggerScenarios":"Running the tiktok following command without valid login cookies (sessionid missing/expired); the universal data snapshot has no isOwner user (logged-out or SSR variant); the /api/user/info fallback call fails or returns no secUid because TikTok redirected to a login wall.","commonSituations":"Stale exported cookies after TikTok rotated the session; loading tiktok.com from a fresh browser profile with no login; TikTok serving an anonymous rehydration payload to datacenter IPs; cookie strategy configured but cookies never imported.","solutions":["Log into tiktok.com in the browser/session this command uses and confirm you see your own profile.","Refresh/re-export the session cookies (ensure sessionid and friends-related cookies are present and not expired).","Re-run the command on the /following page after a normal page load so __UNIVERSAL_DATA_FOR_REHYDRATION__ contains the owner user.","Check that the /api/user/info fallback is reachable (no network/proxy blocking XHR) if cookies exist.","If a TikTok UI change moved secUid out of these fields, update findViewerSecUid in clis/tiktok/following.js."],"exampleFix":"// before: running with no/stale cookies\nawait followingCommand({ limit: 20 }); // AUTH_REQUIRED\n// after: ensure a fresh logged-in session first\nif (!(await isLoggedIn(page))) {\n  await importCookies(page, freshSessionCookies); // includes sessionid\n}\nconst rows = await followingCommand({ limit: 20 });","handlingStrategy":"validation","validationCode":"// pre-check that the session carries login cookies before calling\nconst hasSession = await page.context().cookies().then(cs =>\n  cs.some(c => c.name === 'sessionid' && c.value.length > 0));\nif (!hasSession) throw new Error('refresh TikTok cookies before listing following');","typeGuard":"function hasViewerSecUid(payload) {\n  return typeof payload?.secUid === 'string' && payload.secUid.length > 0;\n}","tryCatchPattern":"try {\n  rows = await followingCommand({ limit: 20 });\n} catch (e) {\n  if (/AUTH_REQUIRED/.test(e.message)) {\n    await refreshSessionCookies(page); // re-export from a logged-in browser\n    rows = await followingCommand({ limit: 20 });\n  } else throw e;\n}","preventionTips":["Refresh session cookies regularly; TikTok rotates sessionid","Load tiktok.com/following interactively once so __UNIVERSAL_DATA_FOR_REHYDRATION__ is populated","Verify login state before running any viewer-scoped command","Avoid anonymous/datacenter proxies that trigger logged-out responses"],"tags":["tiktok","authentication","cookies","login-required"],"backgroundTag":"missing-auth-session","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}