fix: node replacement fails after execution and modal sync (#9269)

## Summary

Fixes two bugs in the node replacement flow: placeholder detection
failing after workflow execution or pack reinstallation, and missing UI
sync in the Errors Tab when replacements are applied from the modal
dialog.

## Changes

- **Placeholder detection**: Node placeholder detection now matches
against `targetTypes` (derived from the replaceable node list built at
workflow load time) instead of relying on `has_errors` flag or
`registered_node_types` lookup. This ensures replacement works reliably
after execution (where `has_errors` gets cleared) and after pack
reinstallation (where the type becomes registered).
- **Modal → Errors Tab sync**: Added
`executionErrorStore.removeMissingNodesByType()` call in
`MissingNodesContent.vue` after replacement, so the Errors Tab reflects
changes immediately without requiring a page reload.

## Review Focus

- `collectAllNodes` predicate change in `useNodeReplacement.ts`: now
uses `targetTypes.has(originalType)` to find nodes by their original
serialized type. This is independent of runtime state like `has_errors`
or `registered_node_types`.
- `executionErrorStore.removeMissingNodesByType` call timing in
`MissingNodesContent.vue` — runs synchronously after
`replaceNodesInPlace` resolves, before auto-close logic.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9269-fix-node-replacement-fails-after-execution-and-modal-sync-3146d73d365081218398c961639b450f)
by [Unito](https://www.unito.io)
This commit is contained in:
jaeone94
2026-02-28 21:05:58 +09:00
committed by GitHub
parent b80eace639
commit 45f112e226
2 changed files with 25 additions and 4 deletions

View File

@@ -226,11 +226,22 @@ export function useNodeReplacement() {
useWorkflowStore().activeWorkflow?.changeTracker ?? null
changeTracker?.beforeChange()
// Target types come from node_replacements fetched at workflow load time
// and the missing nodes detected at that point — not from the current
// registered_node_types. This ensures replacement still works even if
// the user has since installed the missing node pack.
const targetTypes = new Set(
selectedTypes.map((t) => (typeof t === 'string' ? t : t.type))
)
try {
const placeholders = collectAllNodes(
graph,
(n) => !!n.has_errors && !!n.last_serialization
)
const placeholders = collectAllNodes(graph, (n) => {
if (!n.last_serialization) return false
// Prefer the original serialized type; fall back to the live type
// for nodes whose serialization predates the type field.
const originalType = n.last_serialization.type ?? n.type
return !!originalType && targetTypes.has(originalType)
})
for (const node of placeholders) {
const match = findMatchingType(node, selectedTypes)