From 7802213887f6673004459335c350d0ce25eeba34 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Fri, 25 Jul 2025 12:09:04 -0700 Subject: [PATCH] [enhance] Add rich context to RecursionError messages (#1160) --- src/subgraph/ExecutableNodeDTO.ts | 18 ++++++++++++++++-- src/subgraph/SubgraphNode.ts | 10 +++++++++- test/subgraph/SubgraphMemory.test.ts | 4 +--- test/subgraph/SubgraphNode.test.ts | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/subgraph/ExecutableNodeDTO.ts b/src/subgraph/ExecutableNodeDTO.ts index afa7839965..bdfd612697 100644 --- a/src/subgraph/ExecutableNodeDTO.ts +++ b/src/subgraph/ExecutableNodeDTO.ts @@ -131,7 +131,14 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode { */ resolveInput(slot: number, visited = new Set()): ResolvedInput | undefined { const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[I]${slot}` - if (visited.has(uniqueId)) throw new RecursionError(`While resolving subgraph input [${uniqueId}]`) + if (visited.has(uniqueId)) { + const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ""}` + const pathInfo = this.subgraphNodePath.length > 0 ? ` at path ${this.subgraphNodePath.join(":")}` : "" + throw new RecursionError( + `Circular reference detected while resolving input ${slot} of node ${nodeInfo}${pathInfo}. ` + + `This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`, + ) + } visited.add(uniqueId) const input = this.inputs.at(slot) @@ -195,7 +202,14 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode { */ resolveOutput(slot: number, type: ISlotType, visited: Set): ResolvedInput | undefined { const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[O]${slot}` - if (visited.has(uniqueId)) throw new RecursionError(`While resolving subgraph output [${uniqueId}]`) + if (visited.has(uniqueId)) { + const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ""}` + const pathInfo = this.subgraphNodePath.length > 0 ? ` at path ${this.subgraphNodePath.join(":")}` : "" + throw new RecursionError( + `Circular reference detected while resolving output ${slot} of node ${nodeInfo}${pathInfo}. ` + + `This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`, + ) + } visited.add(uniqueId) // Upstreamed: Bypass nodes are bypassed using the first input with matching type diff --git a/src/subgraph/SubgraphNode.ts b/src/subgraph/SubgraphNode.ts index f1a5a85598..b3597bbb9c 100644 --- a/src/subgraph/SubgraphNode.ts +++ b/src/subgraph/SubgraphNode.ts @@ -293,7 +293,15 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph { /** Internal recursion param. The set of visited nodes. */ visited = new Set(), ): ExecutableLGraphNode[] { - if (visited.has(this)) throw new RecursionError("while flattening subgraph") + if (visited.has(this)) { + const nodeInfo = `${this.id}${this.title ? ` (${this.title})` : ""}` + const subgraphInfo = `'${this.subgraph.name || "Unnamed Subgraph"}'` + const depth = subgraphNodePath.length + throw new RecursionError( + `Circular reference detected at depth ${depth} in node ${nodeInfo} of subgraph ${subgraphInfo}. ` + + `This creates an infinite loop in the subgraph hierarchy.`, + ) + } visited.add(this) const subgraphInstanceIdPath = [...subgraphNodePath, this.id] diff --git a/test/subgraph/SubgraphMemory.test.ts b/test/subgraph/SubgraphMemory.test.ts index cebeb81675..9d3bf73544 100644 --- a/test/subgraph/SubgraphMemory.test.ts +++ b/test/subgraph/SubgraphMemory.test.ts @@ -1,11 +1,9 @@ import { describe, expect, it, vi } from "vitest" import { LGraph } from "@/litegraph" -import { SubgraphNode } from "@/subgraph/SubgraphNode" import { subgraphTest } from "./fixtures/subgraphFixtures" import { - createEventCapture, createTestSubgraph, createTestSubgraphNode, } from "./fixtures/subgraphHelpers" @@ -427,4 +425,4 @@ describe("SubgraphMemory - Performance and Scale", () => { expect(rootGraph.nodes.length).toBe(0) } }) -}) \ No newline at end of file +}) diff --git a/test/subgraph/SubgraphNode.test.ts b/test/subgraph/SubgraphNode.test.ts index a1bfb5893f..aa92ead09d 100644 --- a/test/subgraph/SubgraphNode.test.ts +++ b/test/subgraph/SubgraphNode.test.ts @@ -347,7 +347,7 @@ describe("SubgraphNode Execution", () => { const executableNodes = new Map() expect(() => { subgraphNode.getInnerNodes(executableNodes) - }).toThrow(/while flattening subgraph/i) + }).toThrow(/Circular reference detected.*infinite loop in the subgraph hierarchy/i) }) it("should handle nested subgraph execution", () => {