Comfy-Org / Comfy-Org/ComfyUI_frontend
Preview exposures are never removed when their host SubgraphNode or interior source node is removed
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 699
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 490
Description
Preview exposures in usePreviewExposureStore are only removed on explicit user demotion; no node-removal path cleans them. At `f1bfb313d6`, `removeExposure` has exactly two production callers, both demotion UI paths: `SubgraphEditor.vue:284` and `demoteWidget` (`promotionUtils.ts:462`). `LGraph.removeNode` (`LGraph.ts:1264-1315`) never touches the store.
Two orphan shapes:
1. Host SubgraphNode removed: exposures keyed `rootGraphId -> String(hostId)` are retained. Not re-serialized (serialization reads the store per live host, `SubgraphNode.ts:933-945`), so this shape is a session-scoped leak only.
2. Interior source node removed: the host's exposure keeps referencing a nonexistent `sourceNodeId`. Linked promotions have a pruning path (`pruneDisconnected`, `promotionUtils.ts:643-665`); exposures have no analogue. This shape DOES serialize with the host, so the dangling reference persists across save/load.
Bounded by the root clear: `LGraph.resetAfterClear` clears the store with the pre-reassignment root id (`LGraph.ts:575-582`) on every workflow switch/undo.
Pre-existing at the merge base (`a2603c59a6`), not introduced by #14246. Same lifecycle class as the promoted-WidgetState retention in #15714.
Suggested fix shape: shape 1 belongs next to the released-subgraph teardown in `LGraph.removeNode`; shape 2 needs an exposure-pruning analogue to `pruneDisconnected` on source-node removal, or a liveness filter at read/serialize time.
Contributor guide
Assessment
This issue has not been assessed yet.