Index: src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(revision 14899)
+++ src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(working copy)
@@ -13,6 +13,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -85,6 +86,12 @@
     /** if true, buildTree() does nothing */
     private boolean resetScheduled;
 
+    /** if true, (re-) build is in progress */
+    private boolean buildInProgress;
+
+    /** if true, user expanded or collapsed a node */
+    private boolean manualTreeChange;
+
     /**
      * Constructor
      * @param errors The list of errors
@@ -157,7 +164,18 @@
      * Builds the errors tree
      * @param expandAgain if true, try to expand the same rows as before
      */
-    public void buildTree(boolean expandAgain) {
+    private void buildTree(boolean expandAgain) {
+        buildInProgress = true;
+        buildTreeImpl(expandAgain && manualTreeChange);
+        buildInProgress = false;
+    }
+
+
+    /**
+     * Builds the errors tree
+     * @param expandAgain if true, try to expand the same rows as before
+     */
+    private void buildTreeImpl(boolean expandAgain) {
         if (resetScheduled)
             return;
         final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@@ -164,6 +182,7 @@
 
         if (errors == null || errors.isEmpty()) {
             GuiHelper.runInEDTAndWait(() -> valTreeModel.setRoot(rootNode));
+            manualTreeChange = false;
             return;
         }
 
@@ -205,6 +224,8 @@
             = OsmValidator.getErrorsBySeverityMessageDescription(errors, filterToUse);
 
         final List<TreePath> expandedPaths = new ArrayList<>();
+        final List<TreePath> severityPaths = new ArrayList<>();
+
         for (Entry<Severity, Map<String, Map<String, List<TestError>>>> entry: errorsBySeverityMessageDescription.entrySet()) {
             Severity severity = entry.getKey();
             Map<String, Map<String, List<TestError>>> errorsByMessageDescription = entry.getValue();
@@ -212,9 +233,11 @@
             // Severity node
             final DefaultMutableTreeNode severityNode = new GroupTreeNode(severity);
             rootNode.add(severityNode);
+            TreePath severityPath = new TreePath(new Object[] {rootNode, severityNode});
+            severityPaths.add(severityPath);
 
             if (oldExpandedRows.contains(severity)) {
-                expandedPaths.add(new TreePath(new Object[] {rootNode, severityNode}));
+                expandedPaths.add(severityPath);
             }
 
             final Map<String, List<TestError>> errorsWithEmptyMessageByDescription = errorsByMessageDescription.get("");
@@ -281,8 +304,10 @@
         }
 
         valTreeModel.setRoot(rootNode);
+        AtomicBoolean wasExpanded = new AtomicBoolean();
         for (TreePath path : expandedPaths) {
             this.expandPath(path);
+            wasExpanded.set(true);
         }
 
         if (selPath != null) {
@@ -325,15 +350,28 @@
                     TreePath path = new TreePath(n.getPath());
                     setSelectionPath(path);
                     scrollPathToVisible(path);
+                    wasExpanded.set(true);
                 }
             });
         }
+
         if (selRow >= 0 && selRow < getRowCount() && getSelectionCount() == 0) {
             // fall back: if we cannot find the previously selected entry, select the row by position
             setSelectionRow(selRow);
             scrollRowToVisible(selRow);
+            wasExpanded.set(true);
         }
 
+        if (!wasExpanded.get() && !severityPaths.isEmpty()) {
+            // nothing was expanded until now, try to expand severity
+            manualTreeChange = false;
+            for (TreePath path : severityPaths) {
+                expandPath(path);
+                if (getRowCount() > getVisibleRowCount())
+                    break;
+            }
+        }
+
         invalidationListeners.fireEvent(Runnable::run);
     }
 
@@ -389,6 +427,7 @@
             //TODO: If list is changed because another layer was activated it would be good to store/restore
             // the expanded / selected paths.
             clearSelection();
+            manualTreeChange = false;
             buildTree(false);
         }
     }
@@ -605,4 +644,21 @@
                 error -> error.getPrimitives().stream().anyMatch(p -> p.isDeleted() || p.getDataSet() == null));
     }
 
+    @Override
+    public void fireTreeExpanded(TreePath path) {
+        super.fireTreeExpanded(path);
+        // #17295
+        if (!buildInProgress) {
+            manualTreeChange = true;
+        }
+    }
+
+    @Override
+    public void fireTreeCollapsed(TreePath path) {
+        super.fireTreeCollapsed(path);
+        // #17295
+        if (!buildInProgress) {
+            manualTreeChange = true;
+        }
+    }
 }
