[fix] properly show error states (#5758)

## Summary

I want to take a more general look at `comfyApp.graph.onTrigger` but
this is the cleanest fix I could come up with for #5694.

I will explore simplifying onTrigger in a separate PR.

## Changes

1. Create a `node:slot-errors:changed` trigger.
2. Trigger it if we find any of the node slots have errors.
3. Check each node to see if there is any error present.
4. Add an error class if there are.

## Screenshots (if applicable)

Working error states!

<img width="1049" height="987" alt="Screenshot 2025-09-23 at 8 40 04 PM"
src="https://github.com/user-attachments/assets/30e13283-129c-4d9c-b342-e7037582998a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5758-fix-properly-show-error-states-2786d73d365081cbbf62c314c7f5f380)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
Arjan Singh
2025-09-24 16:34:39 -07:00
committed by GitHub
parent 0fea54c542
commit 1749cfa678
3 changed files with 60 additions and 12 deletions

View File

@@ -307,13 +307,23 @@ watch(
removeSlotError(node)
const nodeErrors = lastNodeErrors?.[node.id]
if (!nodeErrors) continue
let slotErrorsChanged = false
for (const error of nodeErrors.errors) {
if (error.extra_info && error.extra_info.input_name) {
const inputIndex = node.findInputSlot(error.extra_info.input_name)
if (inputIndex !== -1) {
node.inputs[inputIndex].hasErrors = true
}
}
if (!error.extra_info?.input_name) continue
const inputIndex = node.findInputSlot(error.extra_info.input_name)
if (inputIndex === -1) continue
node.inputs[inputIndex].hasErrors = true
slotErrorsChanged = true
}
// Trigger Vue node data update if slot errors changed
if (slotErrorsChanged && comfyApp.graph.onTrigger) {
comfyApp.graph.onTrigger('node:slot-errors:changed', {
nodeId: node.id
})
}
}

View File

@@ -435,6 +435,28 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
})
}
}
} else if (
action === 'node:slot-errors:changed' &&
param &&
typeof param === 'object'
) {
const event = param as { nodeId: string | number }
const nodeId = String(event.nodeId)
const litegraphNode = nodeRefs.get(nodeId)
const currentData = vueNodeData.get(nodeId)
if (litegraphNode && currentData) {
// Re-extract slot data with updated hasErrors properties
vueNodeData.set(nodeId, {
...currentData,
inputs: litegraphNode.inputs
? [...litegraphNode.inputs]
: undefined,
outputs: litegraphNode.outputs
? [...litegraphNode.outputs]
: undefined
})
}
}
// Call original trigger handler if it exists

View File

@@ -136,7 +136,11 @@ import { computed, inject, onErrorCaptured, onMounted, provide, ref } from 'vue'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import {
type INodeInputSlot,
type INodeOutputSlot,
LiteGraph
} from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
import { TransformStateKey } from '@/renderer/core/layout/injectionKeys'
@@ -200,9 +204,21 @@ const hasExecutionError = computed(
)
// Computed error states for styling
const hasAnyError = computed(
(): boolean => !!(hasExecutionError.value || nodeData.hasErrors || error)
)
const hasAnyError = computed((): boolean => {
return (
!!hasExecutionError.value ||
!!nodeData.hasErrors ||
!!error ||
// Type assertions needed because VueNodeData.inputs/outputs are typed as unknown[]
// but at runtime they contain INodeInputSlot/INodeOutputSlot objects
!!nodeData.inputs?.some(
(slot) => (slot as INodeInputSlot)?.hasErrors ?? false
) ||
!!nodeData.outputs?.some(
(slot) => (slot as INodeOutputSlot)?.hasErrors ?? false
)
)
})
const bypassed = computed((): boolean => nodeData.mode === 4)
@@ -263,7 +279,7 @@ const { latestPreviewUrl, shouldShowPreviewImg } = useNodePreviewState(
const borderClass = computed(() => {
if (hasAnyError.value) {
return 'border-error'
return 'border-red-500 dark-theme:border-red-500'
}
if (executing.value) {
return 'border-blue-500'
@@ -276,7 +292,7 @@ const outlineClass = computed(() => {
return undefined
}
if (hasAnyError.value) {
return 'outline-error'
return 'outline-red-500'
}
if (executing.value) {
return 'outline-blue-500'