{"record":{"id":"a956fc88f3e41849","repo":"jackwener/OpenCLI","slug":"probe-detail-a956fc","errorCode":null,"errorMessage":"${probe.detail}","messagePattern":"\\$\\{probe\\.detail\\}","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/v2ex/auth.js","lineNumber":26,"sourceCode":"}\n\nasync function verifyV2exIdentity(page) {\n  if (!await hasV2exAuthCookie(page)) {\n    throw new AuthRequiredError('v2ex.com', 'V2EX A2 session cookie missing — anonymous');\n  }\n  await page.goto('https://www.v2ex.com/');\n  await page.wait(1);\n  const probe = await page.evaluate(`\n    (() => {\n      const link = document.querySelector('#Top a[href^=\"/member/\"]');\n      if (!link) return { kind: 'auth', detail: 'V2EX top bar has no member link — anonymous session' };\n      const href = link.getAttribute('href') || '';\n      const username = (link.innerText || href.replace('/member/', '')).trim();\n      if (!username) return { kind: 'auth', detail: 'V2EX member link present but username empty' };\n      return { ok: true, username };\n    })()\n  `);\n  if (probe?.kind === 'auth') throw new AuthRequiredError('v2ex.com', probe.detail);\n  if (!probe?.ok) throw new CommandExecutionError(`Unexpected V2EX probe: ${JSON.stringify(probe)}`);\n  return { username: probe.username };\n}\n\nregisterSiteAuthCommands({\n  site: 'v2ex',\n  domain: 'v2ex.com',\n  loginUrl: 'https://www.v2ex.com/signin',\n  columns: ['username'],\n  quickCheck: hasV2exAuthCookie,\n  verify: verifyV2exIdentity,\n  poll: async (page) => {\n    if (!await hasV2exAuthCookie(page)) {\n      throw new AuthRequiredError('v2ex.com', 'Waiting for V2EX A2 session cookie');\n    }\n    return verifyV2exIdentity(page);\n  },\n});","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/v2ex/auth.js#L8-L44","documentation":"verifyV2exIdentity probes the V2EX top bar inside the browser page. If the probe returns kind:'auth' (e.g. no member link, or a member link with an empty username), the library throws AuthRequiredError for v2ex.com with the probe's detail message. It signals that the current browser session is not (or no longer) a logged-in V2EX session, so the identity cannot be verified and the user must authenticate. This is an expected, controlled error used by the site-auth polling flow.","triggerScenarios":"Calling verifyV2exIdentity (or the v2ex auth verify/poll command) when page.evaluate returns {kind:'auth', detail:...}: (1) no #Top a[href^=\"/member/\"] element exists (anonymous session or V2EX layout change), or (2) the member link exists but its innerText and href-derived username are both empty.","commonSituations":"User never logged in via the loginUrl flow; the A2 session cookie expired so V2EX renders the logged-out top bar; Cloudflare or a bot-challenge page replaced the real homepage DOM; V2EX redesigns the #Top nav so the member-link selector no longer matches.","solutions":["Run the v2ex login command to complete the browser sign-in flow at https://www.v2ex.com/signin until the A2 cookie and member link are present.","Verify the A2 cookie exists and is non-empty (page.getCookies for https://www.v2ex.com); re-login if it is missing or expired.","Manually visit v2ex.com in the automation browser to clear any Cloudflare challenge, then retry the verify command.","If logged in but the error persists, check whether V2EX changed its top-bar markup and update the selector '#Top a[href^=\"/member/\"]'."],"exampleFix":"// before: polling verify while anonymous\nawait verifyV2exIdentity(page); // AuthRequiredError\n// after: ensure login first\nif (!await hasV2exAuthCookie(page)) {\n  await openLoginPage('https://www.v2ex.com/signin'); // user completes login\n}\nconst { username } = await verifyV2exIdentity(page);","handlingStrategy":"try-catch","validationCode":"const cookies = await page.getCookies({ url: 'https://www.v2ex.com' });\nconst loggedIn = cookies.some(c => c.name === 'A2' && c.value);\nif (!loggedIn) throw new Error('Run v2ex login first: no A2 session cookie');","typeGuard":"function isAuthProbe(p) { return !!p && typeof p === 'object' && p.kind === 'auth' && typeof p.detail === 'string'; }\nfunction isOkProbe(p) { return !!p && typeof p === 'object' && p.ok === true && typeof p.username === 'string' && p.username.length > 0; }","tryCatchPattern":"try {\n  const { username } = await verifyV2exIdentity(page);\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    // launch login flow at https://www.v2ex.com/signin and retry after login\n  } else throw e;\n}","preventionTips":["Always run the v2ex login/verify flow before commands that require an authenticated session.","Check for a non-empty A2 cookie before invoking verification.","Manually clear Cloudflare challenges in the automation browser if the top bar renders empty.","Pin/monitor V2EX top-bar markup changes that would break the member-link selector."],"tags":["auth","browser-automation","session-expired"],"backgroundTag":"browser-session-not-authenticated","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}