Index: src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(revision 14886)
+++ src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(working copy)
@@ -85,6 +85,10 @@
     /** if true, buildTree() does nothing */
     private boolean resetScheduled;
 
+    private boolean buildingTree;
+
+    private boolean manualExpand;
+
     /**
      * Constructor
      * @param errors The list of errors
@@ -149,7 +153,16 @@
     /**
      * Builds the errors tree
      */
-    public void buildTree() {
+    private void buildTree() {
+        buildingTree = true;
+        buildTree2();
+        buildingTree = false;
+    }
+
+    /**
+     * Builds the errors tree
+     */
+    private void buildTree2() {
         if (resetScheduled)
             return;
         final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@@ -156,6 +169,7 @@
 
         if (errors == null || errors.isEmpty()) {
             GuiHelper.runInEDTAndWait(() -> valTreeModel.setRoot(rootNode));
+            manualExpand = false;
             return;
         }
 
@@ -165,25 +179,26 @@
 
         // Remember the currently expanded rows
         Set<Object> oldExpandedRows = new HashSet<>();
-        Enumeration<TreePath> expanded = getExpandedDescendants(new TreePath(getRoot()));
-        if (expanded != null) {
-            while (expanded.hasMoreElements()) {
-                TreePath path = expanded.nextElement();
-                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
-                Object userObject = node.getUserObject();
-                if (userObject instanceof Severity) {
-                    oldExpandedRows.add(userObject);
-                } else if (userObject instanceof String) {
-                    String msg = (String) userObject;
-                    int index = msg.lastIndexOf(" (");
-                    if (index > 0) {
-                        msg = msg.substring(0, index);
+        if (manualExpand) {
+            Enumeration<TreePath> expanded = getExpandedDescendants(new TreePath(getRoot()));
+            if (expanded != null) {
+                while (expanded.hasMoreElements()) {
+                    TreePath path = expanded.nextElement();
+                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
+                    Object userObject = node.getUserObject();
+                    if (userObject instanceof Severity) {
+                        oldExpandedRows.add(userObject);
+                    } else if (userObject instanceof String) {
+                        String msg = (String) userObject;
+                        int index = msg.lastIndexOf(" (");
+                        if (index > 0) {
+                            msg = msg.substring(0, index);
+                        }
+                        oldExpandedRows.add(msg);
                     }
-                    oldExpandedRows.add(msg);
                 }
             }
         }
-
         Predicate<TestError> filterToUse = e -> !e.isIgnored();
         if (!ValidatorPrefHelper.PREF_OTHER.get()) {
             filterToUse = filterToUse.and(e -> e.getSeverity() != Severity.OTHER);
@@ -195,6 +210,7 @@
             = 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();
@@ -202,9 +218,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("");
@@ -271,15 +289,25 @@
         }
 
         valTreeModel.setRoot(rootNode);
+        boolean hasExpanded = false;
         for (TreePath path : expandedPaths) {
             this.expandPath(path);
+            hasExpanded = true;
         }
 
+        if (!hasExpanded && !severityPaths.isEmpty()) {
+            manualExpand = false;
+            for (TreePath path : severityPaths) {
+                expandPath(path);
+                if (getRowCount() > getVisibleRowCount())
+                    break;
+            }
+        }
+        // try to reselect previously selected row. May not work if tree structure changed too much.
         if (selRow >= 0 && selRow < getRowCount()) {
             setSelectionRow(selRow);
             scrollRowToVisible(selRow);
         }
-
         invalidationListeners.fireEvent(Runnable::run);
     }
 
@@ -330,6 +358,8 @@
         this.errors = errors != null ? errors : new ArrayList<>();
         sortErrors();
         if (isVisible()) {
+            manualExpand = false;
+            clearSelection();
             buildTree();
         }
     }
@@ -367,8 +397,8 @@
     public void selectRelatedErrors(final Collection<OsmPrimitive> primitives) {
         final List<TreePath> paths = new ArrayList<>();
         walkAndSelectRelatedErrors(new TreePath(getRoot()), new HashSet<>(primitives)::contains, paths);
-        getSelectionModel().clearSelection();
-        getSelectionModel().setSelectionPaths(paths.toArray(new TreePath[0]));
+        clearSelection();
+        setSelectionPaths(paths.toArray(new TreePath[0]));
         // make sure that first path is visible
         if (!paths.isEmpty()) {
             scrollPathToVisible(paths.get(0));
@@ -546,4 +576,11 @@
                 error -> error.getPrimitives().stream().anyMatch(p -> p.isDeleted() || p.getDataSet() == null));
     }
 
+    @Override
+    public void fireTreeExpanded(TreePath path) {
+        super.fireTreeExpanded(path);
+        if (!buildingTree) {
+            manualExpand = true;
+        }
+    }
 }
