diff --git a/core/modules/migrate/src/Tests/MigrateTestBase.php b/core/modules/migrate/src/Tests/MigrateTestBase.php
index 1fda383..139bc2f 100644
--- a/core/modules/migrate/src/Tests/MigrateTestBase.php
+++ b/core/modules/migrate/src/Tests/MigrateTestBase.php
@@ -11,6 +11,7 @@
 use Drupal\migrate\Entity\Migration;
 use Drupal\migrate\MigrateExecutable;
 use Drupal\migrate\MigrateMessageInterface;
+use Drupal\migrate\Plugin\MigrateIdMapInterface;
 use Drupal\migrate\Row;
 use Drupal\simpletest\KernelTestBase;
 
@@ -198,4 +199,26 @@ public function stopCollectingMessages() {
     $this->collectMessages = FALSE;
   }
 
+  /**
+   * Records a failure in the map table of a specific migration in order to
+   * test scenarios which require a failed row.
+   *
+   * @param string|\Drupal\migrate\Entity\MigrationInterface $migration
+   *   The migration entity, or its ID.
+   * @param array $row
+   *   The raw source row which "failed".
+   * @param int $status
+   *   (optional) The failure status. Should be one of the
+   *   MigrateIdMapInterface::STATUS_* constants.
+   */
+  protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) {
+    if (is_string($migration)) {
+      $migration = Migration::load($migration);
+    }
+    /** @var \Drupal\migrate\Entity\MigrationInterface $migration */
+    $destination = array_map(function() { return NULL; }, $migration->getDestinationPlugin()->getIds());
+    $row = new Row($row, $migration->getSourcePlugin()->getIds());
+    $migration->getIdMap()->saveIdMapping($row, $destination, $status);
+  }
+
 }
diff --git a/core/modules/taxonomy/migration_templates/d6_term_node.yml b/core/modules/taxonomy/migration_templates/d6_term_node.yml
index 01578b1..349bcd3 100644
--- a/core/modules/taxonomy/migration_templates/d6_term_node.yml
+++ b/core/modules/taxonomy/migration_templates/d6_term_node.yml
@@ -7,7 +7,14 @@ builder:
 source:
   plugin: d6_term_node
 process:
-  nid: nid
+  nid:
+    -
+      plugin: migration
+      migration: d6_node:*
+      source: nid
+    -
+      plugin: skip_on_empty
+      method: row
   type: type
   # The actual field name is dynamic and will be added by the builder.
 destination:
diff --git a/core/modules/taxonomy/migration_templates/d6_term_node_revision.yml b/core/modules/taxonomy/migration_templates/d6_term_node_revision.yml
index e9ceadd..06e801a 100644
--- a/core/modules/taxonomy/migration_templates/d6_term_node_revision.yml
+++ b/core/modules/taxonomy/migration_templates/d6_term_node_revision.yml
@@ -7,7 +7,14 @@ builder:
 source:
   plugin: d6_term_node_revision
 process:
-  vid: vid
+  vid:
+    -
+      plugin: migration
+      migration: d6_node:*
+      source: vid
+    -
+      plugin: skip_on_empty
+      method: row
   type: type
   # The actual field name is dynamic and will be added by the builder.
 destination:
diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php
index 7c4ad18..8bf9e17 100644
--- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php
+++ b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\taxonomy\Tests\Migrate\d6;
 
+use Drupal\migrate\Entity\Migration;
+use Drupal\migrate\Plugin\MigrateIdMapInterface;
+use Drupal\migrate\Row;
 use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
 use Drupal\node\Entity\Node;
 
@@ -30,13 +33,14 @@ protected function setUp() {
     $this->installSchema('node', ['node_access']);
     $this->migrateContent();
     $this->migrateTaxonomy();
-    $this->executeMigrations(['d6_term_node:*']);
   }
 
   /**
    * Tests the Drupal 6 term-node association to Drupal 8 migration.
    */
   public function testTermNode() {
+    $this->executeMigrations(['d6_term_node:*']);
+
     $this->container->get('entity.manager')
       ->getStorage('node')
       ->resetCache([1, 2]);
@@ -51,4 +55,20 @@ public function testTermNode() {
     $this->assertIdentical('3', $node->vocabulary_2_i_1_[1]->target_id);
   }
 
+  /**
+   * Tests that term associations are ignored when they belong to nodes which
+   * were not migrated.
+   */
+  public function testSkipNonExistentNode() {
+    // Node 2 is migrated by d6_node__story, but we need to pretend that it
+    // failed, so record that in the map table.
+    $this->mockFailure('d6_node__story', ['nid' => 2]);
+
+    // d6_term_node__2 should skip over node 2 (a.k.a. revision 3) because,
+    // according to the map table, it failed.
+    $migration = Migration::load('d6_term_node__2');
+    $this->executeMigration($migration);
+    $this->assertNull($migration->getIdMap()->lookupDestinationId(['vid' => 3])[0]);
+  }
+
 }
