{"record":{"id":"eb65e2e4a5651f14","repo":"jackwener/OpenCLI","slug":"probe-detail-eb65e2","errorCode":null,"errorMessage":"${probe.detail}","messagePattern":"\\$\\{probe\\.detail\\}","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/band/auth.js","lineNumber":44,"sourceCode":"          if (!node || typeof node !== 'object' || seen.has(node)) continue;\n          seen.add(node);\n          if (Array.isArray(node)) { stack.push(...node); continue; }\n          const u = node.user || node.me || node.currentUser;\n          if (u && (u.user_no || u.user_id || u.userId || u.id)) {\n            userId = String(u.user_no || u.user_id || u.userId || u.id);\n            break;\n          }\n          for (const v of Object.values(node)) if (v && typeof v === 'object') stack.push(v);\n        }\n      } catch {}\n      if (!userId) {\n        const el = document.querySelector('[data-user-no], [data-user_no]');\n        userId = el?.getAttribute('data-user-no') || el?.getAttribute('data-user_no') || '';\n      }\n      return { ok: true, user_id: userId };\n    })()\n  `);\n  if (probe?.kind === 'auth') throw new AuthRequiredError('band.us', probe.detail);\n  if (!probe?.ok) throw new CommandExecutionError(`Unexpected Band probe: ${JSON.stringify(probe)}`);\n  return { user_id: probe.user_id };\n}\n\nregisterSiteAuthCommands({\n  site: 'band',\n  domain: 'band.us',\n  loginUrl: 'https://auth.band.us/login',\n  columns: ['user_id'],\n  quickCheck: hasBandSessionCookie,\n  verify: verifyBandIdentity,\n  poll: async (page) => {\n    if (!await hasBandSessionCookie(page)) {\n      throw new AuthRequiredError('band.us', 'Waiting for Band band_session cookie');\n    }\n    return verifyBandIdentity(page);\n  },\n});","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/band/auth.js#L26-L62","documentation":"verifyBandIdentity() throws AuthRequiredError when the in-page probe reports kind='auth', propagating probe.detail as the message — meaning after navigating to band.us/feed the page redirected to a login/auth flow (e.g. location.href matched auth.band.us/login).","triggerScenarios":"band_session cookie exists but is stale/invalid so band.us redirects to auth.band.us/login; partial session where some cookies were cleared; SSO flow requiring re-consent.","commonSituations":"Expired session cookie that still exists; logging out in another tab; Band changing its auth redirect behavior.","solutions":["Re-run the band login/auth flow to refresh the session","Clear band.us cookies and log in fresh if a stale cookie keeps causing redirects","Read probe.detail (in the error message) for the specific auth failure reason"],"exampleFix":"// before\nbandWhoami() // stale cookie -> redirect to auth.band.us/login\n// after\nawait bandLogoutAndClearCookies();\nawait bandLogin();\nawait bandWhoami();","handlingStrategy":"fallback","validationCode":"const resp = await fetch('https://www.band.us/feed', { redirect: 'manual' });\nif (String(resp.status).startsWith('3') && String(resp.headers.get('location') || '').includes('auth.band.us')) {\n  await runBandLogin();\n}","typeGuard":null,"tryCatchPattern":"try {\n  await bandCommand(args);\n} catch (err) {\n  if (err instanceof AuthRequiredError && /auth\\.band\\.us|login/i.test(err.message)) {\n    await clearBandCookies();\n    await runBandLogin();\n    return bandCommand(args);\n  }\n  throw err;\n}","preventionTips":["Refresh the session periodically instead of letting cookies go stale","Detect redirects to auth.band.us in your own flow and re-login proactively","Clear stale band.us cookies before logging in again"],"tags":["authentication","session","redirect"],"backgroundTag":"auth-session-expired","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}