[enhance] Add rich context to RecursionError messages (#1160)

This commit is contained in:
Christian Byrne
2025-07-25 12:09:04 -07:00
committed by GitHub
parent 83795d5db1
commit 7802213887
4 changed files with 27 additions and 7 deletions

View File

@@ -131,7 +131,14 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
*/
resolveInput(slot: number, visited = new Set<string>()): 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<string>): 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

View File

@@ -293,7 +293,15 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
/** Internal recursion param. The set of visited nodes. */
visited = new Set<SubgraphNode>(),
): 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]

View File

@@ -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)
}
})
})
})

View File

@@ -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", () => {