fix: fix error overlay and TabErrors filtering for nested subgraphs (#9129)

## Summary

Fix error overlays not showing on subgraph container nodes and nested
error cards not appearing in the Errors tab when a node inside a
subgraph fails.

## Changes

- **What**: Error overlay and Errors tab filtering now use full
hierarchical execution IDs (e.g. `65:70`) instead of local node IDs,
enabling correct ancestor detection at any nesting depth
- Added `getExecutionIdByNode` to
[graphTraversalUtil.ts](src/utils/graphTraversalUtil.ts) to compute a
node's full execution ID chain from the root graph
- Added `errorAncestorExecutionIds` computed set and
`isContainerWithInternalError(node)` helper to
[executionErrorStore.ts](src/stores/executionErrorStore.ts) for O(1)
container checks
- Updated `hasAnyError` in
[LGraphNode.vue](src/extensions/vueNodes/components/LGraphNode.vue) to
use the new store helper
- Fixed `isErrorInSelection` in
[useErrorGroups.ts](src/components/rightSidePanel/errors/useErrorGroups.ts)
to use full execution IDs for selected containers

## Review Focus

- `errorAncestorExecutionIds` is rebuilt reactively whenever the active
errors change — confirm this is efficient enough given typical error
counts
- `getExecutionIdByNode` walks up the graph hierarchy; verify the base
case (root graph, no parent) is handled correctly

## Screenshots


https://github.com/user-attachments/assets/b5be5892-80a9-4e5e-8b6f-fe754b4ebc4e



https://github.com/user-attachments/assets/92ff12b3-3bc9-4f02-ba4a-e2c7384bafe5



https://github.com/user-attachments/assets/be8e95be-ac8c-4699-9be9-b11902294bda



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9129-fix-fix-error-overlay-and-TabErrors-filtering-for-nested-subgraphs-3106d73d365081c1875bc1a3c89eae29)
by [Unito](https://www.unito.io)
This commit is contained in:
jaeone94
2026-02-23 19:52:45 +09:00
committed by GitHub
parent d6c902187d
commit ddcfdb924d
6 changed files with 80 additions and 16 deletions

View File

@@ -10,11 +10,13 @@ import type {
PromptError
} from '@/schemas/apiSchema'
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
import type { NodeLocatorId } from '@/types/nodeIdentification'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { NodeExecutionId, NodeLocatorId } from '@/types/nodeIdentification'
import {
executionIdToNodeLocatorId,
forEachNode,
getNodeByExecutionId
getNodeByExecutionId,
getExecutionIdByNode
} from '@/utils/graphTraversalUtil'
/**
@@ -186,9 +188,32 @@ export const useExecutionErrorStore = defineStore('executionError', () => {
return nodeError.errors.some((e) => e.extra_info?.input_name === slotName)
}
function hasInternalErrorForNode(nodeId: string | number): boolean {
const prefix = `${nodeId}:`
return allErrorExecutionIds.value.some((id) => id.startsWith(prefix))
/**
* Set of all execution ID prefixes derived from active error nodes,
* including the error nodes themselves.
*
* Example: error at "65:70:63" → Set { "65", "65:70", "65:70:63" }
*/
const errorAncestorExecutionIds = computed<Set<NodeExecutionId>>(() => {
const ids = new Set<NodeExecutionId>()
for (const executionId of allErrorExecutionIds.value) {
const parts = executionId.split(':')
// Add every prefix including the full ID (error leaf node itself)
for (let i = 1; i <= parts.length; i++) {
ids.add(parts.slice(0, i).join(':'))
}
}
return ids
})
/** True if the node has errors inside it at any nesting depth. */
function isContainerWithInternalError(node: LGraphNode): boolean {
if (!app.rootGraph) return false
const execId = getExecutionIdByNode(app.rootGraph, node)
if (!execId) return false
return errorAncestorExecutionIds.value.has(execId)
}
/**
@@ -275,6 +300,7 @@ export const useExecutionErrorStore = defineStore('executionError', () => {
// Lookup helpers
getNodeErrors,
slotHasError,
hasInternalErrorForNode
errorAncestorExecutionIds,
isContainerWithInternalError
}
})