Skip to content

Commit 7802213

Browse files
[enhance] Add rich context to RecursionError messages (#1160)
1 parent 83795d5 commit 7802213

File tree

4 files changed

+27
-7
lines changed

4 files changed

+27
-7
lines changed

src/subgraph/ExecutableNodeDTO.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,14 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
131131
*/
132132
resolveInput(slot: number, visited = new Set<string>()): ResolvedInput | undefined {
133133
const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[I]${slot}`
134-
if (visited.has(uniqueId)) throw new RecursionError(`While resolving subgraph input [${uniqueId}]`)
134+
if (visited.has(uniqueId)) {
135+
const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ""}`
136+
const pathInfo = this.subgraphNodePath.length > 0 ? ` at path ${this.subgraphNodePath.join(":")}` : ""
137+
throw new RecursionError(
138+
`Circular reference detected while resolving input ${slot} of node ${nodeInfo}${pathInfo}. ` +
139+
`This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`,
140+
)
141+
}
135142
visited.add(uniqueId)
136143

137144
const input = this.inputs.at(slot)
@@ -195,7 +202,14 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
195202
*/
196203
resolveOutput(slot: number, type: ISlotType, visited: Set<string>): ResolvedInput | undefined {
197204
const uniqueId = `${this.subgraphNode?.subgraph.id}:${this.node.id}[O]${slot}`
198-
if (visited.has(uniqueId)) throw new RecursionError(`While resolving subgraph output [${uniqueId}]`)
205+
if (visited.has(uniqueId)) {
206+
const nodeInfo = `${this.node.id}${this.node.title ? ` (${this.node.title})` : ""}`
207+
const pathInfo = this.subgraphNodePath.length > 0 ? ` at path ${this.subgraphNodePath.join(":")}` : ""
208+
throw new RecursionError(
209+
`Circular reference detected while resolving output ${slot} of node ${nodeInfo}${pathInfo}. ` +
210+
`This creates an infinite loop in link resolution. UniqueID: [${uniqueId}]`,
211+
)
212+
}
199213
visited.add(uniqueId)
200214

201215
// Upstreamed: Bypass nodes are bypassed using the first input with matching type

src/subgraph/SubgraphNode.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,15 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
293293
/** Internal recursion param. The set of visited nodes. */
294294
visited = new Set<SubgraphNode>(),
295295
): ExecutableLGraphNode[] {
296-
if (visited.has(this)) throw new RecursionError("while flattening subgraph")
296+
if (visited.has(this)) {
297+
const nodeInfo = `${this.id}${this.title ? ` (${this.title})` : ""}`
298+
const subgraphInfo = `'${this.subgraph.name || "Unnamed Subgraph"}'`
299+
const depth = subgraphNodePath.length
300+
throw new RecursionError(
301+
`Circular reference detected at depth ${depth} in node ${nodeInfo} of subgraph ${subgraphInfo}. ` +
302+
`This creates an infinite loop in the subgraph hierarchy.`,
303+
)
304+
}
297305
visited.add(this)
298306

299307
const subgraphInstanceIdPath = [...subgraphNodePath, this.id]

test/subgraph/SubgraphMemory.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { describe, expect, it, vi } from "vitest"
22

33
import { LGraph } from "@/litegraph"
4-
import { SubgraphNode } from "@/subgraph/SubgraphNode"
54

65
import { subgraphTest } from "./fixtures/subgraphFixtures"
76
import {
8-
createEventCapture,
97
createTestSubgraph,
108
createTestSubgraphNode,
119
} from "./fixtures/subgraphHelpers"
@@ -427,4 +425,4 @@ describe("SubgraphMemory - Performance and Scale", () => {
427425
expect(rootGraph.nodes.length).toBe(0)
428426
}
429427
})
430-
})
428+
})

test/subgraph/SubgraphNode.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ describe("SubgraphNode Execution", () => {
347347
const executableNodes = new Map()
348348
expect(() => {
349349
subgraphNode.getInnerNodes(executableNodes)
350-
}).toThrow(/while flattening subgraph/i)
350+
}).toThrow(/Circular reference detected.*infinite loop in the subgraph hierarchy/i)
351351
})
352352

353353
it("should handle nested subgraph execution", () => {

0 commit comments

Comments
 (0)