@@ -47,6 +47,31 @@ export class BrowserbaseService {
4747 return process . env . BROWSERBASE_PROJECT_ID || '' ;
4848 }
4949
50+ /**
51+ * Stagehand sometimes has no active page (or the page gets closed mid-run),
52+ * which causes errors like: "No Page found for awaitActivePage: no page available".
53+ * Ensure there's at least one non-closed page available, and create one if needed.
54+ */
55+ private async ensureActivePage ( stagehand : Stagehand ) {
56+ const MAX_WAIT_MS = 5000 ;
57+ const POLL_MS = 250 ;
58+ const startedAt = Date . now ( ) ;
59+
60+ while ( Date . now ( ) - startedAt < MAX_WAIT_MS ) {
61+ // Stagehand's Page type doesn't always expose Playwright's `isClosed()` in typings.
62+ // We still want to filter out closed pages at runtime when possible.
63+ const pages = stagehand . context . pages ( ) . filter ( ( p ) => {
64+ const maybeIsClosed = ( p as { isClosed ?: ( ) => boolean } ) . isClosed ;
65+ return typeof maybeIsClosed === 'function' ? ! maybeIsClosed ( ) : true ;
66+ } ) ;
67+ if ( pages [ 0 ] ) return pages [ 0 ] ;
68+ await delay ( POLL_MS ) ;
69+ }
70+
71+ // Last resort: create a page (may still fail if the CDP session already died)
72+ return await stagehand . context . newPage ( ) ;
73+ }
74+
5075 // ===== Organization Context Management =====
5176
5277 async getOrCreateOrgContext (
@@ -250,10 +275,7 @@ export class BrowserbaseService {
250275 const stagehand = await this . createStagehand ( sessionId ) ;
251276
252277 try {
253- const page = stagehand . context . pages ( ) [ 0 ] ;
254- if ( ! page ) {
255- throw new Error ( 'No page found in browser session' ) ;
256- }
278+ const page = await this . ensureActivePage ( stagehand ) ;
257279
258280 await page . goto ( url , {
259281 waitUntil : 'domcontentloaded' ,
@@ -278,6 +300,18 @@ export class BrowserbaseService {
278300 isLoggedIn : result . isLoggedIn ,
279301 username : result . username ,
280302 } ;
303+ } catch ( err ) {
304+ const message = err instanceof Error ? err . message : String ( err ) ;
305+ const isNoPage =
306+ message . includes ( 'awaitActivePage' ) ||
307+ message . includes ( 'no page available' ) ||
308+ message . includes ( 'No page found' ) ;
309+ if ( isNoPage ) {
310+ throw new Error (
311+ 'Browser session ended before we could verify login status. Please retry.' ,
312+ ) ;
313+ }
314+ throw err ;
281315 } finally {
282316 await stagehand . close ( ) ;
283317 }
@@ -696,10 +730,7 @@ export class BrowserbaseService {
696730 const stagehand = await this . createStagehand ( sessionId ) ;
697731
698732 try {
699- const page = stagehand . context . pages ( ) [ 0 ] ;
700- if ( ! page ) {
701- throw new Error ( 'No page found in browser session' ) ;
702- }
733+ let page = await this . ensureActivePage ( stagehand ) ;
703734
704735 // Navigate to target URL
705736 await page . goto ( targetUrl , {
@@ -746,6 +777,9 @@ export class BrowserbaseService {
746777
747778 // Evaluate if the automation fulfills the task requirements BEFORE taking screenshot
748779 if ( taskContext ) {
780+ // Re-acquire page in case the agent closed/replaced it during execution
781+ page = await this . ensureActivePage ( stagehand ) ;
782+
749783 const evaluationSchema = z . object ( {
750784 passes : z
751785 . boolean ( )
@@ -808,6 +842,7 @@ Only pass if there is clear evidence the requirement is properly configured and
808842 }
809843
810844 // Only take screenshot if evaluation passed (or no task context)
845+ page = await this . ensureActivePage ( stagehand ) ;
811846 const screenshot = await page . screenshot ( {
812847 type : 'jpeg' ,
813848 quality : 80 ,
@@ -822,10 +857,17 @@ Only pass if there is clear evidence the requirement is properly configured and
822857 } ;
823858 } catch ( err ) {
824859 this . logger . error ( 'Failed to execute automation' , err ) ;
860+ const message = err instanceof Error ? err . message : String ( err ) ;
861+ const isNoPage =
862+ message . includes ( 'awaitActivePage' ) ||
863+ message . includes ( 'no page available' ) ||
864+ message . includes ( 'No page found' ) ;
825865 return {
826866 success : false ,
827- error :
828- err instanceof Error ? err . message : 'Failed to execute automation' ,
867+ needsReauth : isNoPage ? true : undefined ,
868+ error : isNoPage
869+ ? 'Browser session ended before we could capture evidence. Please retry.'
870+ : message ,
829871 } ;
830872 } finally {
831873 await stagehand . close ( ) ;
0 commit comments