Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 17293)
+++ src/org/openstreetmap/josm/data/validation/OsmValidator.java	(working copy)
@@ -44,6 +44,7 @@
 import org.openstreetmap.josm.data.validation.tests.ConditionalKeys;
 import org.openstreetmap.josm.data.validation.tests.ConnectivityRelations;
 import org.openstreetmap.josm.data.validation.tests.CrossingWays;
+import org.openstreetmap.josm.data.validation.tests.DirectionNodes;
 import org.openstreetmap.josm.data.validation.tests.DuplicateNode;
 import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
 import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
@@ -151,6 +152,7 @@
         RightAngleBuildingTest.class, // 3700 .. 3799
         SharpAngles.class, // 3800 .. 3899
         ConnectivityRelations.class, // 3900 .. 3999
+        DirectionNodes.class, // 4000-4099
     };
 
     /**
Index: src/org/openstreetmap/josm/data/validation/tests/DirectionNodes.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/DirectionNodes.java	(nonexistent)
+++ src/org/openstreetmap/josm/data/validation/tests/DirectionNodes.java	(working copy)
@@ -0,0 +1,83 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.TestError;
+
+/**
+ * Find nodes with direction tag and invalid number of parent ways or position in way. See #20019.
+ * @author Gerd Petermann
+ * @since xxx
+ */
+public class DirectionNodes extends Test {
+    private static final int MULLTIPLE_WAYS_CODE = 4000;
+    private static final int END_NODE_CODE = 4001;
+    private static final int NO_WAY_CODE = 4002;
+
+    private static final Pattern KEY_PATTERN = Pattern.compile(".*[:]?direction");
+
+    /**
+     * Construct a new {@code DirectionNodes} object
+     */
+    public DirectionNodes() {
+        super(tr("Direction nodes"), tr("Check for nodes which have a direction"));
+    }
+
+    @Override
+    public void visit(Node n) {
+        if (!n.isUsable() || !n.isTagged())
+            return;
+        for (Entry<String, String> entry : n.getKeys().entrySet()) {
+            if (("forward".equals(entry.getValue()) || "backward".equals(entry.getValue()))
+                    && KEY_PATTERN.matcher(entry.getKey()).matches()) {
+                checkParents(n, entry.getKey());
+            }
+        }
+    }
+
+    private void checkParents(Node n, String key) {
+        final List<Way> ways = new ArrayList<>();
+        boolean hasWays = false;
+        for (Way w : n.getParentWays()) {
+            if (w.hasKey("highway", "railway", "waterway")) {
+                ways.add(w);
+            }
+            hasWays = true;
+        }
+
+        if (ways.isEmpty() && !n.isOutsideDownloadArea() && n.getDataSet().getDataSourceArea() != null) {
+            if (!hasWays) {
+                errors.add(TestError.builder(this, Severity.WARNING, NO_WAY_CODE)
+                        .primitives(n)
+                        .message(tr("Unconnected node with {0}", key)).build());
+            }
+        } else if (ways.size() == 1) {
+            Way w = ways.get(0);
+            if (w.firstNode() == n || w.lastNode() == n) {
+                errors.add(TestError.builder(this, Severity.WARNING, END_NODE_CODE)
+                        .primitives(n, w)
+                        .highlight(n)
+                        .message(tr("Node with {0} on end of way", key)).build());
+            }
+        } else if (ways.size() > 1) {
+            List<OsmPrimitive> primitives = new ArrayList<>();
+            primitives.add(n);
+            primitives.addAll(ways);
+            errors.add(TestError.builder(this, Severity.WARNING, MULLTIPLE_WAYS_CODE)
+                    .primitives(primitives)
+                    .highlight(n)
+                    .message(tr("Node with {0} on a connection of multiple ways", key)).build());
+        }
+    }
+}
