-
Notifications
You must be signed in to change notification settings - Fork 479
Description
Bug: Stream data gets GC'd while user is still on the session page
Description
When a stream completes (or is stopped/errored) in a chat session, the scheduleGC function sets a 50-minute timer to delete the stream from the in-memory Map. However, this happens regardless of whether the user is still viewing the session page.
As a result, if the user stays on the same session page after a stream finishes and then tries to interact with it (e.g., re-subscribing via subscribe()), the stream data may have already been garbage-collected, causing the UI to lose the cached streaming state.
Root Cause
In src/lib/stream-session-manager.ts, the scheduleGC function only checks two conditions before deleting a stream entry:
- The stream reference still matches the one stored in the map (
current === stream) - The stream is no longer in
'active'phase
It does not check whether the session is the one the user is currently viewing.
// src/lib/stream-session-manager.ts
function scheduleGC(stream: ActiveStream) {
if (stream.gcTimer) clearTimeout(stream.gcTimer);
stream.gcTimer = setTimeout(() => {
const map = getStreamsMap();
const current = map.get(stream.sessionId);
if (current === stream && current.snapshot.phase !== 'active') {
map.delete(stream.sessionId); // ← deletes even while user is on this page
}
}, GC_DELAY_MS);
}Expected Behavior
The stream entry for the currently active session should not be deleted by GC while the user is still on that session page. GC should only clean up the stream after the user navigates away from the page (i.e., the session becomes truly inactive).
Proposed Fix
Introduce a module-level activeSessionId variable and three helper functions:
setActiveSessionId(id: string)— called on page mountgetActiveSessionId(): string | null— getterclearActiveSessionId()— called on page unmount
Then update scheduleGC to skip deletion when stream.sessionId === activeSessionId.
A useEffect in src/app/chat/[id]/page.tsx calls setActiveSessionId(id) on mount and clearActiveSessionId() on unmount:
useEffect(() => {
setActiveSessionId(id);
return () => {
clearActiveSessionId();
};
}, [id]);Files Involved
| File | Change |
|---|---|
src/lib/stream-session-manager.ts |
Add activeSessionId state + getter/setter, update scheduleGC |
src/app/chat/[id]/page.tsx |
Call setActiveSessionId/clearActiveSessionId on mount/unmount |