fix: make SubgraphNode.graph nullable to allow proper cleanup (#8180)

## Summary

Fix `SubgraphNode.graph` property to match `LGraphNode` lifecycle
contract. Previously declared as `override readonly graph` via
constructor parameter promotion, which prevented `LGraph.remove()` from
setting `node.graph = null`.

## Changes

- Remove `readonly` from `SubgraphNode.graph` constructor parameter
- Add `override graph: GraphOrSubgraph | null` as class property  
- Add `NullGraphError` guard in `rootGraph` getter with node ID for
debugging
- Add null guards in `ExecutableNodeDTO.resolveInput` and
`imagePreviewStore.revokeSubgraphPreviews`
- Add test verifying `rootGraph` throws after node removal

## Testing

- Existing subgraph tests pass
- New test confirms `NullGraphError` is thrown when accessing
`rootGraph` on removed node

---------

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Christian Byrne
2026-02-03 16:18:48 -08:00
committed by GitHub
parent 66e776774a
commit ffc0bf00ef
7 changed files with 35 additions and 13 deletions

View File

@@ -299,9 +299,10 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
* Does not recurse to contents of nested subgraphs.
*/
function revokeSubgraphPreviews(subgraphNode: SubgraphNode) {
const graphId = subgraphNode.graph.isRootGraph
? ''
: subgraphNode.graph.id + ':'
const { graph } = subgraphNode
if (!graph) return
const graphId = graph.isRootGraph ? '' : graph.id + ':'
revokePreviewsByLocatorId(graphId + subgraphNode.id)
for (const node of subgraphNode.subgraph.nodes) {
revokePreviewsByLocatorId(subgraphNode.subgraph.id + node.id)

View File

@@ -94,7 +94,7 @@ describe('useSubgraphStore', () => {
//mock canvas to provide a minimal subgraphNode
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
const graph = subgraphNode.graph
const graph = subgraphNode.graph!
graph.add(subgraphNode)
vi.mocked(comfyApp.canvas).selectedItems = new Set([subgraphNode])
vi.mocked(comfyApp.canvas)._serializeItems = vi.fn(() => {