mirror of
https://github.com/Comfy-Org/comfy-typescript-sdk.git
synced 2026-11-04 23:47:27 +00:00
[PR #15] [MERGED] fix: read-idle timeout on SSE so a stalled stream can't hang events() #25
Labels
No labels
audit:ai-readiness
audit:community
audit:github
audit:health-scoring
audit:policy
audit:release
audit:supply-chain
bug
cursor-review-pin
effort:medium
effort:small
effort:trivial
priority:nice-to-have
priority:recommended
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-typescript-sdk#25
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-typescript-sdk/pull/15
Author: @wei-hai
Created: 7/22/2026
Status: ✅ Merged
Merged: 7/22/2026
Merged by: @wei-hai
Base:
main← Head:fix/sse-idle-timeout📝 Commits (1)
6f2e83afix: read-idle timeout on SSE so a stalled stream can't hang events()📊 Changes
2 files changed (+61 additions, -1 deletions)
View changed files
📝
src/low/sse.test.ts(+22 -0)📝
src/low/sse.ts(+39 -1)📄 Description
Found running a real >10-minute job against prod: the SSE stream went silent at ~3 min (no frames, no keepalive, no close — a "zombie" connection), and
job.events()hung — still blocked minutes after the job had already completed.poll/wait/runwere unaffected (the job itself finished fine).Root cause
iterateSseread the fetch body with no idle timeout, so it blocks forever on a stalled-but-open connection. The high-levelJob.events()reconnect/poll-fallback only runs when the stream ends (the async iterator returns or throws), which never happens for a zombie →events()hangs.Fix
Add a byte-level read-idle timeout to
iterateSse(defaultSSE_IDLE_TIMEOUT_MS, well above the server keepalive interval). It resets on any bytes — so keepalive comments (whicheventsource-parserdrops as non-messages) still count as liveness, and only a truly silent connection trips it. A stalled stream now errors, whichJob.events()already catches and turns into a poll (terminal?) / reconnect.Tests
Added a regression test: a stream that emits two frames then holds open + silent →
iterateSseerrors quickly (~idle timeout) instead of hanging, after delivering the pre-stall frames. Full suite green; tsc/oxfmt/hygiene clean.🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.