Index: src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java
===================================================================
--- src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java	(revision 14779)
+++ 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
@@ -50,9 +59,6 @@
      */
     public static final String PREF_FILTER_BY_SELECTION = PREFIX + ".selectionFilter";
 
-    /**
-     * Constructs a new {@code PresetPrefHelper}.
-     */
     public ValidatorPrefHelper() {
         super(MapCSSTagChecker.ENTRIES_PREF_KEY, SourceType.TAGCHECKER_RULE);
     }
Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14779)
+++ 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;
@@ -88,8 +87,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 HashMap<String, String> ignoredErrors = new HashMap<>();
     /**
      * All registered tests
      */
@@ -204,11 +202,21 @@
     private static void loadIgnoredErrors() {
         ignoredErrors.clear();
         if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) {
+            Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(map -> {
+                ignoredErrors.putAll(map);
+            });
             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 +236,80 @@
      * @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) {
+        ignoredErrors.put(s, description);
+    }
+
+    /**
      * 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 HashMap<String, String> getIgnoredErrors() {
+        return ignoredErrors;
+    }
+
+    /**
+     * 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);
+        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/ValidatorListManagementDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java	(nonexistent)
+++ src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java	(working copy)
@@ -0,0 +1,213 @@
+// 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.event.ActionEvent;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeMap;
+
+import javax.swing.ImageIcon;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+
+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()) {
+            dispose();
+        } else {
+            super.buttonAction(buttonIndex, evt);
+        }
+    }
+
+    private DefaultMutableTreeNode inTree(DefaultMutableTreeNode root, String name) {
+        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 JTree with a list
+     * @return &lttype&gtlist as a {@code JTree}
+     */
+    public JTree buildList() {
+        TreeMap<String, String> map = new TreeMap<>();
+        if ("ignore".equals(type)) {
+            HashMap<String, String> tmap;
+            tmap = OsmValidator.getIgnoredErrors();
+            if (tmap.isEmpty()) {
+                OsmValidator.initialize();
+                tmap = OsmValidator.getIgnoredErrors();
+            }
+            map.putAll(tmap);
+        } else {
+            Logging.error(tr("Cannot understand the following type: {0}", type));
+            return null;
+        }
+        DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("{0} list", type));
+
+        for (String key : map.keySet()) {
+            String value = map.get(key);
+            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);
+        }
+        JTree tree = new JTree(root);
+        tree.setRootVisible(false);
+        return tree;
+    }
+
+    /**
+     * 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/gui/dialogs/ValidatorDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java	(revision 14779)
+++ 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)
