@@ -13,6 +13,7 @@ import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
1313const logger = createLogger ( 'ExecutionStreamReconnectAPI' )
1414
1515const POLL_INTERVAL_MS = 500
16+ const MAX_POLL_DURATION_MS = 10 * 60 * 1000 // 10 minutes
1617
1718function isTerminalStatus ( status : ExecutionStreamStatus ) : boolean {
1819 return status === 'complete' || status === 'error' || status === 'cancelled'
@@ -70,10 +71,13 @@ export async function GET(
7071
7172 const encoder = new TextEncoder ( )
7273
74+ // Hoisted so cancel() can signal the polling loop to stop
75+ let closed = false
76+
7377 const stream = new ReadableStream < Uint8Array > ( {
7478 async start ( controller ) {
7579 let lastEventId = fromEventId
76- let closed = false
80+ const pollDeadline = Date . now ( ) + MAX_POLL_DURATION_MS
7781
7882 const enqueue = ( text : string ) => {
7983 if ( closed ) return
@@ -101,8 +105,8 @@ export async function GET(
101105 return
102106 }
103107
104- // Poll for new events until execution completes
105- while ( ! closed ) {
108+ // Poll for new events until execution completes or deadline is reached
109+ while ( ! closed && Date . now ( ) < pollDeadline ) {
106110 await new Promise ( ( resolve ) => setTimeout ( resolve , POLL_INTERVAL_MS ) )
107111 if ( closed ) return
108112
@@ -127,6 +131,13 @@ export async function GET(
127131 return
128132 }
129133 }
134+
135+ // Deadline reached — close gracefully
136+ if ( ! closed ) {
137+ logger . warn ( 'Reconnection stream poll deadline reached' , { executionId } )
138+ enqueue ( 'data: [DONE]\n\n' )
139+ controller . close ( )
140+ }
130141 } catch ( error ) {
131142 logger . error ( 'Error in reconnection stream' , {
132143 executionId,
@@ -140,6 +151,7 @@ export async function GET(
140151 }
141152 } ,
142153 cancel ( ) {
154+ closed = true
143155 logger . info ( 'Client disconnected from reconnection stream' , { executionId } )
144156 } ,
145157 } )
0 commit comments