Index: src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java
===================================================================
--- src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java	(revision 14786)
+++ src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java	(working copy)
@@ -44,6 +44,15 @@
     /** The preferences for ignored severity other */
     public static final BooleanProperty PREF_OTHER = new BooleanProperty(PREFIX + ".other", false);
 
+    /** The preferences key for the ignorelist */
+    public static final String PREF_IGNORELIST = PREFIX + ".ignorelist";
+
+    /** The preferences key for the ignorelist backup */
+    public static final String PREF_IGNORELIST_BACKUP = PREFIX + ".ignorelist.bak";
+
+    /** The preferences key for whether or not the ignorelist backup should be cleared on start */
+    public static final BooleanProperty PREF_IGNORELIST_KEEP_BACKUP = new BooleanProperty(PREFIX + ".ignorelist.bak.keep", false);
+
     /**
      * The preferences key for enabling the permanent filtering
      * of the displayed errors in the tree regarding the current selection
Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14786)
+++ src/org/openstreetmap/josm/data/validation/OsmValidator.java	(working copy)
@@ -7,7 +7,6 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -17,9 +16,12 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
+import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -27,6 +29,10 @@
 import java.util.stream.Collectors;
 
 import javax.swing.JOptionPane;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
 
 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
@@ -88,8 +94,7 @@
     /** Grid detail, multiplier of east,north values for valuable cell sizing */
     private static double griddetail;
 
-    private static final Collection<String> ignoredErrors = new TreeSet<>();
-
+    private static final SortedMap<String, String> ignoredErrors = new TreeMap<>();
     /**
      * All registered tests
      */
@@ -169,7 +174,7 @@
     public static void initialize() {
         checkValidatorDir();
         initializeGridDetail();
-        loadIgnoredErrors(); //FIXME: load only when needed
+        loadIgnoredErrors();
     }
 
     /**
@@ -204,11 +209,18 @@
     private static void loadIgnoredErrors() {
         ignoredErrors.clear();
         if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) {
+            Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(ignoredErrors::putAll);
             Path path = Paths.get(getValidatorDir()).resolve("ignorederrors");
             try {
                 if (path.toFile().exists()) {
                     try {
-                        ignoredErrors.addAll(Files.readAllLines(path, StandardCharsets.UTF_8));
+                        TreeSet<String> treeSet = new TreeSet<>();
+                        treeSet.addAll(Files.readAllLines(path, StandardCharsets.UTF_8));
+                        treeSet.forEach(ignore -> ignoredErrors.putIfAbsent(ignore, ""));
+
+                        saveIgnoredErrors();
+                        Files.deleteIfExists(path);
+
                     } catch (FileNotFoundException e) {
                         Logging.debug(Logging.getErrorMessage(e));
                     } catch (IOException e) {
@@ -228,29 +240,198 @@
      * @see TestError#getIgnoreSubGroup()
      */
     public static void addIgnoredError(String s) {
-        ignoredErrors.add(s);
+        addIgnoredError(s, "");
     }
 
     /**
+     * Adds an ignored error
+     * @param s The ignore group / sub group name
+     * @param description What the error actually is
+     * @see TestError#getIgnoreGroup()
+     * @see TestError#getIgnoreSubGroup()
+     */
+    public static void addIgnoredError(String s, String description) {
+        if (description == null) description = "";
+        ignoredErrors.put(s, description);
+        if (s.split(":(r|w|n)_[0-9]+($|:)").length == 1) {
+            cleanupIgnoredErrors();
+        }
+    }
+
+    /**
+     *  Make sure that we don't keep single entries for a "group ignore" or
+     *  multiple different entries for the single entries that are in the same group.
+     */
+    private static void cleanupIgnoredErrors() {
+        if (ignoredErrors.size() > 1) {
+            List<String> toRemove = new ArrayList<>();
+
+            Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator();
+            Entry<String, String> last = iter.next();
+            while (iter.hasNext()) {
+                Entry<String, String> entry = iter.next();
+                if (entry.getKey().startsWith(last.getKey())) {
+                    toRemove.add(entry.getKey());
+                } else {
+                    last = entry;
+                }
+            }
+            toRemove.forEach(ignoredErrors::remove);
+            Map<String, String> tmap = buildIgnore(buildJTreeList());
+            if (tmap != null && !tmap.isEmpty()) {
+                ignoredErrors.clear();
+                ignoredErrors.putAll(tmap);
+            }
+        }
+    }
+
+    /**
      * Check if a error should be ignored
      * @param s The ignore group / sub group name
      * @return <code>true</code> to ignore that error
      */
     public static boolean hasIgnoredError(String s) {
-        return ignoredErrors.contains(s);
+        return ignoredErrors.containsKey(s);
     }
 
     /**
-     * Saves the names of the ignored errors to a file
+     * Get the list of all ignored errors
+     * @return The <code>Collection&ltString&gt</code> of errors that are ignored
      */
+    public static SortedMap<String, String> getIgnoredErrors() {
+        return ignoredErrors;
+    }
+
+    /**
+     * Build a JTree with a list
+     * @return &lttype&gtlist as a {@code JTree}
+     */
+    public static JTree buildJTreeList() {
+        DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Ignore list"));
+
+        for (Entry<String, String> e: ignoredErrors.entrySet()) {
+            String key = e.getKey();
+            String value = e.getValue();
+            String[] osmobjects = key.split(":(r|w|n)_");
+            DefaultMutableTreeNode trunk;
+            DefaultMutableTreeNode branch;
+
+            if (value != null && !value.isEmpty()) {
+                trunk = inTree(root, value);
+                branch = inTree(trunk, osmobjects[0]);
+                trunk.add(branch);
+            } else {
+                trunk = inTree(root, osmobjects[0]);
+                branch = trunk;
+            }
+            for (int i = 1; i < osmobjects.length; i++) {
+                String osmid = osmobjects[i];
+                int index = key.indexOf(osmid);
+                char type = key.charAt(index - 2);
+                DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(type + "_" + osmid);
+                branch.add(leaf);
+            }
+            root.add(trunk);
+        }
+        return new JTree(root);
+    }
+
+    private static DefaultMutableTreeNode inTree(DefaultMutableTreeNode root, String name) {
+        @SuppressWarnings("unchecked")
+        Enumeration<TreeNode> trunks = root.children();
+        while (trunks.hasMoreElements()) {
+            TreeNode ttrunk = trunks.nextElement();
+            if (ttrunk instanceof DefaultMutableTreeNode) {
+                DefaultMutableTreeNode trunk = (DefaultMutableTreeNode) ttrunk;
+                if (name.equals(trunk.getUserObject())) {
+                    return trunk;
+                }
+            }
+        }
+        return new DefaultMutableTreeNode(name);
+    }
+
+    /**
+     * Build a {@code HashMap} from a tree of ignored errors
+     * @param tree The JTree of ignored errors
+     * @return A {@code HashMap} of the ignored errors for comparison
+     */
+    public static Map<String, String> buildIgnore(JTree tree) {
+        TreeModel model = tree.getModel();
+        DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
+        return buildIgnore(model, root);
+    }
+
+    private static Map<String, String> buildIgnore(TreeModel model, DefaultMutableTreeNode node) {
+        HashMap<String, String> rHashMap = new HashMap<>();
+
+        String osmids = node.getUserObject().toString();
+        String description = "";
+
+        if (!model.getRoot().equals(node)) description = ((DefaultMutableTreeNode) node.getParent()).getUserObject().toString();
+        if (!osmids.matches("^[0-9]+_.*")) osmids = "";
+
+        for (int i = 0; i < model.getChildCount(node); i++) {
+            DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(node, i);
+            if (model.getChildCount(child) == 0) {
+                String ignoreName = child.getUserObject().toString();
+                if (ignoreName.matches("^(r|w|n)_.*")) {
+                    osmids += ":" + child.getUserObject().toString();
+                } else if (ignoreName.matches("^[0-9]+_.*")) {
+                    rHashMap.put(ignoreName, description);
+                }
+            } else {
+                rHashMap.putAll(buildIgnore(model, child));
+            }
+        }
+        if (!osmids.isEmpty() && osmids.indexOf(':') != 0) rHashMap.put(osmids, description);
+        return rHashMap;
+    }
+
+    /**
+     * Reset the error list by deleting {@code validator.ignorelist}
+     */
+    public static void resetErrorList() {
+        saveIgnoredErrors();
+        backupErrorList();
+        Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null);
+        OsmValidator.initialize();
+    }
+
+    /**
+     * Restore the error list by copying {@code validator.ignorelist.bak} to
+     * {@code validator.ignorelist}
+     */
+    public static void restoreErrorList() {
+        saveIgnoredErrors();
+        List<Map<String, String>> tlist = Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP);
+        backupErrorList();
+        Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, tlist);
+        OsmValidator.initialize();
+    }
+
+    private static void backupErrorList() {
+        List<Map<String, String>> tlist = Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null);
+        Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP, tlist);
+    }
+
+    /**
+     * Saves the names of the ignored errors to a preference
+     */
     public static void saveIgnoredErrors() {
-        try (PrintWriter out = new PrintWriter(new File(getValidatorDir(), "ignorederrors"), StandardCharsets.UTF_8.name())) {
-            for (String e : ignoredErrors) {
-                out.println(e);
+        cleanupIgnoredErrors();
+        List<Map<String, String>> list = new ArrayList<>();
+        list.add(ignoredErrors);
+        int i = 0;
+        while (i < list.size()) {
+            if (list.get(i) == null || list.get(i).isEmpty()) {
+                list.remove(i);
+                continue;
             }
-        } catch (IOException e) {
-            Logging.error(e);
+            i++;
         }
+        if (list.isEmpty()) list = null;
+        Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, list);
     }
 
     /**
Index: src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java	(revision 14786)
+++ src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java	(working copy)
@@ -63,6 +63,7 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
+import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
 import org.xml.sax.SAXException;
 
@@ -85,6 +86,8 @@
     private final SideButton fixButton;
     /** The ignore button */
     private final SideButton ignoreButton;
+    /** The reset ignorelist button */
+    private final SideButton ignorelistManagement;
     /** The select button */
     private final SideButton selectButton;
     /** The lookup button */
@@ -174,9 +177,32 @@
             });
             ignoreButton.setEnabled(false);
             buttons.add(ignoreButton);
+
+            if (!ValidatorPrefHelper.PREF_IGNORELIST_KEEP_BACKUP.get()) {
+                // Clear the backup ignore list
+                Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP, null);
+            }
+            ignorelistManagement = new SideButton(new AbstractAction() {
+                {
+                    putValue(NAME, tr("Manage Ignore"));
+                    putValue(SHORT_DESCRIPTION, tr("Manage the ignore list"));
+                    new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true);
+                }
+
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    ValidatorListManagementDialog dialog = new ValidatorListManagementDialog("Ignore");
+                    if (dialog.getValue() == 1) {
+                        // TODO save
+                    }
+                }
+            });
+            buttons.add(ignorelistManagement);
         } else {
             ignoreButton = null;
+            ignorelistManagement = null;
         }
+
         createLayout(tree, true, buttons);
     }
 
@@ -245,7 +271,7 @@
 
             Object mainNodeInfo = node.getUserObject();
             if (!(mainNodeInfo instanceof TestError)) {
-                Set<String> state = new HashSet<>();
+                Set<Pair<String, String>> state = new HashSet<>();
                 // ask if the whole set should be ignored
                 if (asked == JOptionPane.DEFAULT_OPTION) {
                     String[] a = new String[] {tr("Whole group"), tr("Single elements"), tr("Nothing")};
@@ -257,10 +283,10 @@
                     ValidatorTreePanel.visitTestErrors(node, err -> {
                         err.setIgnored(true);
                         changed.set(true);
-                        state.add(node.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup());
+                        state.add(new Pair<>(node.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup(), err.getMessage()));
                     }, processedNodes);
-                    for (String s : state) {
-                        OsmValidator.addIgnoredError(s);
+                    for (Pair<String, String> s : state) {
+                        OsmValidator.addIgnoredError(s.a, s.b);
                     }
                     continue;
                 } else if (asked == JOptionPane.CANCEL_OPTION || asked == JOptionPane.CLOSED_OPTION) {
@@ -271,7 +297,7 @@
             ValidatorTreePanel.visitTestErrors(node, error -> {
                 String state = error.getIgnoreState();
                 if (state != null) {
-                    OsmValidator.addIgnoredError(state);
+                    OsmValidator.addIgnoredError(state, error.getMessage());
                 }
                 changed.set(true);
                 error.setIgnored(true);
@@ -287,7 +313,6 @@
     /**
      * Sets the selection of the map to the current selected items.
      */
-    @SuppressWarnings("unchecked")
     private void setSelectedItems() {
         DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
         if (tree == null || ds == null)
Index: src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java	(nonexistent)
+++ src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java	(working copy)
@@ -0,0 +1,257 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+
+import org.openstreetmap.josm.actions.ValidateAction;
+import org.openstreetmap.josm.data.validation.OsmValidator;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
+
+
+/**
+ * A management window for the validator's ignorelist
+ * @author Taylor Smock
+ * @since xxx
+ */
+public class ValidatorListManagementDialog extends ExtendedDialog {
+    enum BUTTONS {
+        OK(0, tr("OK"), new ImageProvider("ok")),
+        CLEAR(1, tr("Clear All"), new ImageProvider("dialogs", "fix")),
+        RESTORE(2, tr("Restore"), new ImageProvider("copy")),
+        CANCEL(3, tr("Cancel"), new ImageProvider("cancel"));
+
+        private int index;
+        private String name;
+        private ImageIcon icon;
+
+        BUTTONS(int index, String name, ImageProvider image) {
+            this.index = index;
+            this.name = name;
+            this.icon = image.getResource().getImageIcon();
+        }
+
+        public ImageIcon getImageIcon() {
+            return icon;
+        }
+
+        public int getIndex() {
+            return index;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    private static final String[] BUTTON_TEXTS = {BUTTONS.OK.getName(), BUTTONS.CLEAR.getName(),
+            BUTTONS.RESTORE.getName(), BUTTONS.CANCEL.getName()
+    };
+
+    private static final ImageIcon[] BUTTON_IMAGES = {BUTTONS.OK.getImageIcon(), BUTTONS.CLEAR.getImageIcon(),
+            BUTTONS.RESTORE.getImageIcon(), BUTTONS.CANCEL.getImageIcon()
+    };
+
+    private final JPanel panel = new JPanel(new GridBagLayout());
+
+    private final JTree ignoreErrors;
+
+    private final String type;
+
+    /**
+     * Create a new {@link ValidatorListManagementDialog}
+     * @param type The type of list to create (first letter may or may not be
+     * capitalized, it is put into all lowercase after building the title)
+     */
+    public ValidatorListManagementDialog(String type) {
+        super(MainApplication.getMainFrame(), tr("Validator {0} List Management", type), BUTTON_TEXTS, false);
+        this.type = type.toLowerCase(Locale.ENGLISH);
+        setButtonIcons(BUTTON_IMAGES);
+
+        ignoreErrors = buildList();
+        JScrollPane scroll = GuiHelper.embedInVerticalScrollPane(ignoreErrors);
+
+        panel.add(scroll, GBC.eol().fill(GBC.BOTH).anchor(GBC.CENTER));
+        setContent(panel);
+        setDefaultButton(1);
+        setupDialog();
+        showDialog();
+    }
+
+    @Override
+    public void buttonAction(int buttonIndex, ActionEvent evt) {
+        // Currently OK/Cancel buttons do nothing
+        final int answer;
+        if (buttonIndex == BUTTONS.RESTORE.getIndex()) {
+            dispose();
+            answer = rerunValidatorPrompt();
+            if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) {
+                OsmValidator.restoreErrorList();
+            }
+        } else if (buttonIndex == BUTTONS.CLEAR.getIndex()) {
+            dispose();
+            answer = rerunValidatorPrompt();
+            if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) {
+                OsmValidator.resetErrorList();
+            }
+        } else if (buttonIndex == BUTTONS.OK.getIndex()) {
+            Map<String, String> errors = OsmValidator.getIgnoredErrors();
+            Map<String, String> tree = OsmValidator.buildIgnore(ignoreErrors);
+            if (!errors.equals(tree)) {
+                answer = rerunValidatorPrompt();
+                if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) {
+                    OsmValidator.resetErrorList();
+                    tree.forEach((ignore, description) -> {
+                        OsmValidator.addIgnoredError(ignore, description);
+                    });
+                    OsmValidator.saveIgnoredErrors();
+                    OsmValidator.initialize();
+                }
+            }
+            dispose();
+        } else {
+            super.buttonAction(buttonIndex, evt);
+        }
+    }
+
+    /**
+     * Build a JTree with a list
+     * @return &lttype&gtlist as a {@code JTree}
+     */
+    public JTree buildList() {
+        JTree tree;
+
+        if ("ignore".equals(type)) {
+            tree = OsmValidator.buildJTreeList();
+        } else {
+            Logging.error(tr("Cannot understand the following type: {0}", type));
+            return null;
+        }
+        tree.setRootVisible(false);
+        tree.setShowsRootHandles(true);
+        tree.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mousePressed(MouseEvent e) {
+                process(e);
+            }
+
+            @Override
+            public void mouseReleased(MouseEvent e) {
+                process(e);
+            }
+
+            private void process(MouseEvent e) {
+                if (e.isPopupTrigger()) {
+                    TreePath[] paths = tree.getSelectionPaths();
+                    if (paths == null) return;
+                    Rectangle bounds = tree.getUI().getPathBounds(tree, paths[0]);
+                    if (bounds != null) {
+                        JPopupMenu menu = new JPopupMenu();
+                        JMenuItem delete = new JMenuItem(new AbstractAction(tr("Delete")) {
+                            @Override
+                            public void actionPerformed(ActionEvent e1) {
+                                deleteAction(tree, paths);
+                            }
+                        });
+                        menu.add(delete);
+                        menu.show(e.getComponent(), e.getX(), e.getY());
+                    }
+                }
+            }
+        });
+
+        tree.addKeyListener(new KeyListener() {
+
+            @Override
+            public void keyTyped(KeyEvent e) {
+                // Do nothing
+            }
+
+            @Override
+            public void keyPressed(KeyEvent e) {
+                // Do nothing
+            }
+
+            @Override
+            public void keyReleased(KeyEvent e) {
+                TreePath[] paths = tree.getSelectionPaths();
+                if (e.getKeyCode() == KeyEvent.VK_DELETE && paths != null) {
+                    deleteAction(tree, paths);
+                }
+            }
+        });
+        return tree;
+    }
+
+    private void deleteAction(JTree tree, TreePath[] paths) {
+        for (TreePath path : paths) {
+            tree.clearSelection();
+            tree.addSelectionPath(path);
+            DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
+            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
+            node.removeAllChildren();
+            while (node.getChildCount() == 0) {
+                node.removeFromParent();
+                node = parent;
+                if (parent == null || parent.isRoot()) break;
+                parent = (DefaultMutableTreeNode) node.getParent();
+            }
+        }
+        tree.updateUI();
+    }
+
+
+    /**
+     * Prompt to rerun the validator when the ignore list changes
+     * @return {@code JOptionPane.YES_OPTION}, {@code JOptionPane.NO_OPTION},
+     *  or {@code JOptionPane.CANCEL_OPTION}
+     */
+    public int rerunValidatorPrompt() {
+        MapFrame map = MainApplication.getMap();
+        List<TestError> errors = map.validatorDialog.tree.getErrors();
+        ValidateAction validateAction = ValidatorDialog.validateAction;
+        if (!validateAction.isEnabled() || errors == null || errors.isEmpty()) return JOptionPane.NO_OPTION;
+        final int answer = ConditionalOptionPaneUtil.showOptionDialog(
+                "rerun_validation_when_ignorelist_changed",
+                MainApplication.getMainFrame(),
+                tr("{0}Should the validation be rerun?{1}", "<hmtl><h3>", "</h3></html>"),
+                tr("Ignored error filter changed"),
+                JOptionPane.YES_NO_CANCEL_OPTION,
+                JOptionPane.QUESTION_MESSAGE,
+                null,
+                null);
+        if (answer == JOptionPane.YES_OPTION) {
+            validateAction.doValidate(true);
+        }
+        return answer;
+    }
+}
Index: src/org/openstreetmap/josm/spi/preferences/MapListSetting.java
===================================================================
--- src/org/openstreetmap/josm/spi/preferences/MapListSetting.java	(revision 14786)
+++ src/org/openstreetmap/josm/spi/preferences/MapListSetting.java	(working copy)
@@ -6,6 +6,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedMap;
 
 /**
  * Setting containing a {@link List} of {@link Map}s of {@link String} values.
@@ -40,7 +41,7 @@
         if (value.contains(null))
             throw new IllegalArgumentException("Error: Null as list element in preference setting");
         for (Map<String, String> map : value) {
-            if (map.containsKey(null))
+            if (!(map instanceof SortedMap) && map.containsKey(null))
                 throw new IllegalArgumentException("Error: Null as map key in preference setting");
             if (map.containsValue(null))
                 throw new IllegalArgumentException("Error: Null as map value in preference setting");
