{"record":{"id":"20af1a1bd603a3be","repo":"jackwener/OpenCLI","slug":"browser-page-is-missing-a-session","errorCode":null,"errorMessage":"Browser page is missing a session","messagePattern":"Browser page is missing a session","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli.ts","lineNumber":691,"sourceCode":"\nfunction getBrowserSession(command?: Command): string {\n  // The CLI surface is `opencli browser <session> <subcommand>`. main.ts rewrites\n  // argv to insert `--session <name>` before commander parses it; this helper\n  // reads back the rewritten flag.\n  const raw = getCommandOption(command, 'session');\n  if (typeof raw === 'string' && raw.trim()) return raw.trim();\n  throw new Error('<session> is a required positional argument: opencli browser <session> <command>');\n}\n\nfunction getBrowserProfileSelection(command?: Command): ProfileSelection | undefined {\n  const raw = getCommandOption(command, 'profile');\n  return resolveProfileSelection(typeof raw === 'string' && raw.trim() ? raw.trim() : undefined);\n}\n\nfunction getPageSession(page: import('./types.js').IPage): string {\n  const session = (page as unknown as { session?: unknown }).session;\n  if (typeof session === 'string' && session.trim()) return session.trim();\n  throw new Error('Browser page is missing a session');\n}\n\nfunction getPageScope(page: import('./types.js').IPage): string {\n  // Scope is keyed by the SELECTED profile (explicit or preferred), matching\n  // getBrowserPage's targetScope — reading only the explicit contextId would\n  // save and look up the remembered tab under different keys whenever the\n  // profile came from the config default.\n  const { contextId, preferredContextId } = page as unknown as { contextId?: unknown; preferredContextId?: unknown };\n  const selected = typeof contextId === 'string' && contextId.trim()\n    ? contextId.trim()\n    : (typeof preferredContextId === 'string' && preferredContextId.trim() ? preferredContextId.trim() : undefined);\n  return getBrowserScope(getPageSession(page), selected);\n}\n\ntype SnapshotSource = 'dom' | 'ax';\n\nfunction snapshotMetricText(snapshot: unknown): string {\n  return typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot, null, 2);","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/cli.ts#L673-L709","documentation":"A browser page object must carry a `session` string property identifying which browser session it belongs to. getPageSession narrows and trims it, throwing when the property is missing, not a string, or blank. This is an internal invariant: pages returned by the browser layer are expected to always be session-tagged.","triggerScenarios":"A page implementation (custom or mock IPage) does not set `session`; or a session property is an empty/whitespace string; or a page from an older plugin version predates session tagging.","commonSituations":"Custom page adapters not updated after the session-scoping feature; test doubles/stubs for IPage that omit the session field; deserialized page objects losing the property.","solutions":["Ensure the page factory sets `session` (non-empty string) on every IPage it returns","Update custom page/mock implementations to include the session property","Check plugin/adapter versions — an older page object may predate session tagging","If invoking browser commands, use pages obtained from the library's own open/session APIs rather than hand-built objects"],"exampleFix":"// before\nconst page = { url: () => 'about:blank' } as IPage;\n// after\nconst page = { url: () => 'about:blank', session: 'my-session' } as IPage;","handlingStrategy":"type-guard","validationCode":"function assertPageHasSession(page: IPage): string {\n  const s = (page as { session?: unknown }).session;\n  if (typeof s !== 'string' || !s.trim()) throw new Error('page must carry a non-empty session string');\n  return s.trim();\n}","typeGuard":"function hasSession(page: IPage): page is IPage & { session: string } {\n  const s = (page as { session?: unknown }).session;\n  return typeof s === 'string' && s.trim().length > 0;\n}","tryCatchPattern":"try {\n  const session = getPageSession(page);\n} catch (e) {\n  if ((e as Error).message === 'Browser page is missing a session') {\n    throw new Error('Page adapter is outdated: it must set page.session');\n  }\n  throw e;\n}","preventionTips":["Always build pages via the library's own session/open APIs, not ad-hoc objects","Keep custom IPage adapters in sync with library releases (session field is required)","In tests, make mocks implement the full IPage surface including session","Assert page.session right after page creation to catch regressions early"],"tags":["invariant","browser","missing-property"],"backgroundTag":"missing-required-property","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}