diff --git a/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.test.ts b/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.test.ts index 382b00e0fa5..d0a4dcf0165 100644 --- a/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.test.ts +++ b/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.test.ts @@ -431,6 +431,19 @@ describe('getPinDataSize', () => { expect(pinData.pinnedDataByNodeName.value.Other).toBeDefined(); }); + it('skips cleanup when the deleted node has no pinned data', () => { + const { pinData, fire } = createPinDataWithNodesChange(); + pinData.pinNodeData('Other', [{ json: { y: 2 } }] as INodeExecutionData[]); + const hookSpy = vi.fn(); + pinData.onPinnedDataChange(hookSpy); + const before = pinData.pinnedDataByNodeName.value; + + fire({ action: CHANGE_ACTION.DELETE, payload: { id: 'node-id-1', name: 'Target' } }); + + expect(hookSpy).not.toHaveBeenCalled(); + expect(pinData.pinnedDataByNodeName.value).toBe(before); + }); + it('does nothing when DELETE has no node name (removeAllNodes)', () => { const { pinData, fire } = createPinDataWithNodesChange(); pinData.pinNodeData('Target', [{ json: { x: 1 } }] as INodeExecutionData[]); diff --git a/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts b/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts index 60635df91ef..90d7a806774 100644 --- a/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts +++ b/packages/frontend/editor-ui/src/app/stores/workflowDocument/useWorkflowDocumentPinData.ts @@ -227,7 +227,10 @@ export function useWorkflowDocumentPinData(deps: WorkflowDocumentPinDataDeps) { // useWorkflowDocumentNodes via an injected `unpinNodeData` dep; // pulling it into the pinData subscriber means nodes doesn't need // to know about pinData at all (breaks the construction cycle). - if (payload.name) applyUnpin(payload.name); + // Skipped when the node has no pinned data: unpinning unconditionally + // would replace the pin-data object identity (dirtying every computed + // that reads it) and fire a spurious DELETE pin event. + if (payload.name && pinnedDataByNodeName.value[payload.name]) applyUnpin(payload.name); break; } case CHANGE_ACTION.SET: {