Index: trunk/test/unit/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelperTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelperTest.java	(revision 18840)
+++ trunk/test/unit/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelperTest.java	(revision 18842)
@@ -2,7 +2,9 @@
 package org.openstreetmap.josm.gui.dialogs.properties;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.awt.GraphicsEnvironment;
@@ -15,10 +17,15 @@
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.swing.JOptionPane;
 import javax.swing.JTable;
 import javax.swing.table.DefaultTableModel;
 
+import org.awaitility.Awaitility;
+import org.awaitility.Durations;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
@@ -30,4 +37,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
+import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.properties.TagEditHelper.AddTagsDialog;
@@ -35,7 +43,12 @@
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.testutils.annotations.Territories;
 import org.openstreetmap.josm.testutils.mockers.WindowMocker;
+import org.openstreetmap.josm.tools.JosmRuntimeException;
+
+import mockit.Mock;
+import mockit.MockUp;
 
 /**
@@ -104,4 +117,81 @@
             return node;
         }, "junction", "roundabout");
+    }
+
+    /**
+     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/23191>#23191</a>
+     */
+    @Test
+    void testTicket23191() {
+        TestUtils.assumeWorkingJMockit();
+        final TagEditHelper tagEditHelper = newTagEditHelper();
+        final DataSet original = new DataSet();
+        MainApplication.getLayerManager().addLayer(new OsmDataLayer(original, "TagEditHelperTest.testTicket23191_1", null));
+        final Node toSelect = TestUtils.newNode("");
+        original.addPrimitive(toSelect);
+        original.setSelected(toSelect);
+        assertEquals(1, OsmDataManager.getInstance().getInProgressISelection().size());
+        assertTrue(OsmDataManager.getInstance().getInProgressISelection().contains(toSelect));
+
+        final AtomicBoolean canContinue = new AtomicBoolean();
+        final AtomicBoolean showingDialog = new AtomicBoolean();
+
+        // Instantiate the AddTagsDialog where we don't have to worry about race conditions
+        tagEditHelper.sel = OsmDataManager.getInstance().getInProgressSelection();
+        final AddTagsDialog addTagsDialog = tagEditHelper.getAddTagsDialog();
+        tagEditHelper.resetSelection();
+        new MockUp<TagEditHelper>() {
+            @Mock
+            public AddTagsDialog getAddTagsDialog() {
+                return addTagsDialog;
+            }
+        };
+
+        new MockUp<AddTagsDialog>() {
+            @Mock
+            public ExtendedDialog showDialog() {
+                showingDialog.set(true);
+                while (!canContinue.get()) {
+                    synchronized (canContinue) {
+                        try {
+                            canContinue.wait();
+                        } catch (InterruptedException e) {
+                            throw new JosmRuntimeException(e);
+                        }
+                    }
+                }
+                return null;
+            }
+
+            @Mock
+            public int getValue() {
+                return 1;
+            }
+        };
+
+        // Avoid showing the JOption pane
+        Config.getPref().putBoolean("message.properties.selection-changed", false);
+        Config.getPref().putInt("message.properties.selection-changed.value", JOptionPane.YES_OPTION);
+
+        // "Open" the tag edit dialog -- this should technically be in the EDT, but we are mocking the UI parts out,
+        // since the EDT does allow new EDT runnables when showing the add tag dialog
+        Future<?> tagFuture = MainApplication.worker.submit(tagEditHelper::addTag);
+
+        Awaitility.await().atMost(Durations.ONE_SECOND).untilTrue(showingDialog);
+        // This is what remote control will effectively do
+        MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "TagEditHelperTest.testTicket23191_2", null));
+        tagEditHelper.resetSelection();
+
+        // Enter key=value
+        addTagsDialog.keys.setText("building");
+        addTagsDialog.values.setText("yes");
+
+        // Close the tag edit dialog
+        synchronized (canContinue) {
+            canContinue.set(true);
+            canContinue.notifyAll();
+        }
+
+        assertDoesNotThrow(() -> tagFuture.get());
     }
 
