diff --git a/core/core.services.yml b/core/core.services.yml
index 598754e10b4a596efe225095a4b006765d8dd3c3..81cdb36aefbe22abfb16f3482cb529458a88e9c0 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -806,6 +806,8 @@ services:
   router.route_provider.lazy_builder:
     class: Drupal\Core\Routing\RouteProviderLazyBuilder
     arguments: ['@router.route_provider', '@router.builder']
+    tags:
+      - { name: event_subscriber }
   router.route_preloader:
     class: Drupal\Core\Routing\RoutePreloader
     arguments: ['@router.route_provider', '@state', '@cache.bootstrap']
diff --git a/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php b/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php
index 8c59900c82ff38b5f9deed5f97b50ffafd63e922..a2cb8c57851330c28545bd05fa136b6f36d52bf1 100644
--- a/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php
@@ -3,12 +3,13 @@
 namespace Drupal\Core\Routing;
 
 use Symfony\Cmf\Component\Routing\PagedRouteProviderInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
  * A Route Provider front-end for all Drupal-stored routes.
  */
-class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface {
+class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface, EventSubscriberInterface {
 
   /**
    * The route provider service.
@@ -31,6 +32,18 @@ class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, Pag
    */
   protected $rebuilt = FALSE;
 
+  /**
+   * Flag to determine if router is currently being rebuilt.
+   *
+   * Used to prevent recursive router rebuilds during module installation.
+   * Recursive rebuilds can occur when route information is required by alter
+   * hooks that are triggered during a rebuild, for example,
+   * hook_menu_links_discovered_alter().
+   *
+   * @var bool
+   */
+  protected $rebuilding = FALSE;
+
   /**
    * RouteProviderLazyBuilder constructor.
    *
@@ -51,7 +64,7 @@ public function __construct(RouteProviderInterface $route_provider, RouteBuilder
    *   The route provider service.
    */
   protected function getRouteProvider() {
-    if (!$this->rebuilt) {
+    if (!$this->rebuilt && !$this->rebuilding) {
       $this->routeBuilder->rebuild();
       $this->rebuilt = TRUE;
     }
@@ -132,4 +145,27 @@ public function hasRebuilt() {
     return $this->rebuilt;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC][] = ['routerRebuilding', 3000];
+    $events[RoutingEvents::FINISHED][] = ['routerRebuildFinished', -3000];
+    return $events;
+  }
+
+  /**
+   * Sets the router rebuilding flag to TRUE.
+   */
+  public function routerRebuilding() {
+    $this->rebuilding = TRUE;
+  }
+
+  /**
+   * Sets the router rebuilding flag to FALSE.
+   */
+  public function routerRebuildFinished() {
+    $this->rebuilding = FALSE;
+  }
+
 }
diff --git a/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module b/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..7989d765e4e6f77c485bc09bfea69d5097774594
--- /dev/null
+++ b/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Helper module for the lazy route provider tests.
+ */
+
+/**
+ * Implements hook_menu_links_discovered_alter().
+ */
+function lazy_route_provider_install_test_menu_links_discovered_alter(&$links) {
+  $message = \Drupal::state()->get(__FUNCTION__, 'success');
+  try {
+    // Ensure that calling this does not cause a recursive rebuild.
+    \Drupal::service('router.route_provider')->getAllRoutes();
+  }
+  catch (\RuntimeException $e) {
+    $message = 'failed';
+  }
+  \Drupal::state()->set(__FUNCTION__, $message);
+}
diff --git a/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php b/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php
index bc01dc288a51fbf78dbbb3d64db8b02d8c9c2660..cec35a07acc0b7627795ea1f7853f6128461e70f 100644
--- a/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php
+++ b/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php
@@ -23,6 +23,11 @@ public function testInstallation() {
     // we cannot use ::assertEquals().
     $this->assertStringEndsWith('/admin', \Drupal::state()->get('Drupal\lazy_route_provider_install_test\PluginManager'));
     $this->assertStringEndsWith('/router_test/test1', \Drupal::state()->get('router_test_install'));
+    // If there is an exception thrown in rebuilding a route then the state
+    // 'lazy_route_provider_install_test_menu_links_discovered_alter' will be
+    // set.
+    // @see lazy_route_provider_install_test_menu_links_discovered_alter().
+    $this->assertEquals('success', \Drupal::state()->get('lazy_route_provider_install_test_menu_links_discovered_alter', NULL));
   }
 
 }