fix: handle SSE controller errors gracefully

- Add isActive checks before writing to SSE controller
- Wrap controller operations in try/catch to prevent 'already closed' errors
- Fix race condition when client disconnects during SSE streaming
This commit is contained in:
2026-03-06 22:40:57 +01:00
parent f0a967515b
commit 9141edfaa9
2 changed files with 32 additions and 13 deletions

View File

@@ -33,27 +33,39 @@ export async function GET(
}, },
}); });
if (response.ok) { if (response.ok && isActive) {
const data = await response.json(); const data = await response.json();
const dataStr = JSON.stringify(data); const dataStr = JSON.stringify(data);
// Only send if data changed // Only send if data changed
if (dataStr !== lastData) { if (dataStr !== lastData && isActive) {
lastData = dataStr; lastData = dataStr;
try {
controller.enqueue( controller.enqueue(
new TextEncoder().encode(`data: ${dataStr}\n\n`) new TextEncoder().encode(`data: ${dataStr}\n\n`)
); );
} catch (err) {
// Controller closed, ignore
isActive = false;
return;
}
// Stop polling if job is complete // Stop polling if job is complete
if (data.status === "success" || data.status === "failed" || data.status === "cancelled") { if (data.status === "success" || data.status === "failed" || data.status === "cancelled") {
isActive = false; isActive = false;
try {
controller.close(); controller.close();
} catch (err) {
// Already closed, ignore
}
} }
} }
} }
} catch (error) { } catch (error) {
if (isActive) {
console.error("SSE fetch error:", error); console.error("SSE fetch error:", error);
} }
}
}; };
// Initial fetch // Initial fetch

View File

@@ -28,21 +28,28 @@ export async function GET(request: NextRequest) {
}, },
}); });
if (response.ok) { if (response.ok && isActive) {
const data = await response.json(); const data = await response.json();
const dataStr = JSON.stringify(data); const dataStr = JSON.stringify(data);
// Send if data changed // Send if data changed
if (dataStr !== lastData) { if (dataStr !== lastData && isActive) {
lastData = dataStr; lastData = dataStr;
try {
controller.enqueue( controller.enqueue(
new TextEncoder().encode(`data: ${dataStr}\n\n`) new TextEncoder().encode(`data: ${dataStr}\n\n`)
); );
} catch (err) {
// Controller closed, ignore
isActive = false;
}
} }
} }
} catch (error) { } catch (error) {
if (isActive) {
console.error("SSE fetch error:", error); console.error("SSE fetch error:", error);
} }
}
}; };
// Initial fetch // Initial fetch