diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index 79fa2e8136728b60c17a51b153e73fc10abfb1d9..9f0e7fc912df72204be37674e0cf2cfe529e4969 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -6,6 +6,8 @@ services:
       - '@datetime.time'
       - '@event_dispatcher'
       - '@automatic_updates.updater'
+      - '@automatic_updates.cron_updater'
+      - '@config.factory'
       - 24
   automatic_updates.updater:
     class: Drupal\automatic_updates\Updater
diff --git a/src/Event/ReadinessCheckEvent.php b/src/Event/ReadinessCheckEvent.php
index 2b8b9a6f86a4d023f8a631be64fcad0c6e04a929..ebe5706594977110deed28761510bfef390b9835 100644
--- a/src/Event/ReadinessCheckEvent.php
+++ b/src/Event/ReadinessCheckEvent.php
@@ -33,13 +33,24 @@ class ReadinessCheckEvent extends PreOperationStageEvent {
    *
    * @param \Drupal\automatic_updates\Updater $updater
    *   The updater service.
-   * @param string[] $package_versions
-   *   (optional) The desired package versions to update to, keyed by package
+   * @param string[] $project_versions
+   *   (optional) The versions of the packages to update to, keyed by package
    *   name.
    */
-  public function __construct(Updater $updater, array $package_versions = []) {
+  public function __construct(Updater $updater, array $project_versions = []) {
     parent::__construct($updater);
-    $this->packageVersions = $package_versions;
+    if ($project_versions) {
+      if (count($project_versions) !== 1 || !array_key_exists('drupal', $project_versions)) {
+        throw new \InvalidArgumentException("Currently only updates to Drupal core are supported.");
+      }
+      $core_packages = $this->getStage()->getActiveComposer()->getCorePackageNames();
+      // Update all core packages to the same version.
+      $package_versions = array_fill(0, count($core_packages), $project_versions['drupal']);
+      $this->packageVersions = array_combine($core_packages, $package_versions);
+    }
+    else {
+      $this->packageVersions = [];
+    }
   }
 
   /**
diff --git a/src/Form/UpdaterForm.php b/src/Form/UpdaterForm.php
index 42eba8fcf002a33ef7f91603b8724587dd8011d8..52db117b9ecf438a5d6cfe22cce6a5249aab3226 100644
--- a/src/Form/UpdaterForm.php
+++ b/src/Form/UpdaterForm.php
@@ -3,8 +3,10 @@
 namespace Drupal\automatic_updates\Form;
 
 use Drupal\automatic_updates\BatchProcessor;
+use Drupal\automatic_updates\Event\ReadinessCheckEvent;
 use Drupal\automatic_updates\Updater;
 use Drupal\automatic_updates\UpdateRecommender;
+use Drupal\automatic_updates\Validation\ReadinessTrait;
 use Drupal\automatic_updates\Validation\ReadinessValidationManager;
 use Drupal\Core\Batch\BatchBuilder;
 use Drupal\Core\Form\FormBase;
@@ -15,6 +17,7 @@ use Drupal\Core\Url;
 use Drupal\system\SystemManager;
 use Drupal\update\UpdateManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Defines a form to update Drupal core.
@@ -24,6 +27,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 class UpdaterForm extends FormBase {
 
+  use ReadinessTrait;
+
   /**
    * The updater service.
    *
@@ -45,6 +50,13 @@ class UpdaterForm extends FormBase {
    */
   protected $readinessValidationManager;
 
+  /**
+   * The event dispatcher service.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
   /**
    * Constructs a new UpdaterForm object.
    *
@@ -54,11 +66,14 @@ class UpdaterForm extends FormBase {
    *   The updater service.
    * @param \Drupal\automatic_updates\Validation\ReadinessValidationManager $readiness_validation_manager
    *   The readiness validation manager service.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher service.
    */
-  public function __construct(StateInterface $state, Updater $updater, ReadinessValidationManager $readiness_validation_manager) {
+  public function __construct(StateInterface $state, Updater $updater, ReadinessValidationManager $readiness_validation_manager, EventDispatcherInterface $event_dispatcher) {
     $this->updater = $updater;
     $this->state = $state;
     $this->readinessValidationManager = $readiness_validation_manager;
+    $this->eventDispatcher = $event_dispatcher;
   }
 
   /**
@@ -75,7 +90,8 @@ class UpdaterForm extends FormBase {
     return new static(
       $container->get('state'),
       $container->get('automatic_updates.updater'),
-      $container->get('automatic_updates.readiness_validation_manager')
+      $container->get('automatic_updates.readiness_validation_manager'),
+      $container->get('event_dispatcher')
     );
   }
 
@@ -182,17 +198,18 @@ class UpdaterForm extends FormBase {
       ],
     ];
 
-    // @todo Add a hasErrors() or getErrors() method to
-    // ReadinessValidationManager to make validation more introspectable.
-    // Re-running the readiness checks now should mean that when we display
-    // cached errors in automatic_updates_page_top(), we'll see errors that
-    // were raised during this run, instead of any previously cached results.
-    $errors = $this->readinessValidationManager->run()
-      ->getResults(SystemManager::REQUIREMENT_ERROR);
-
-    if (empty($errors)) {
+    $results = $this->getReadinessErrors($recommended_release->getVersion());
+    if (empty($results)) {
       $form['actions'] = $this->actions($form_state);
     }
+    else {
+      $this->messenger()->addError($this->getFailureMessageForSeverity(SystemManager::REQUIREMENT_ERROR));
+      foreach ($results as $result) {
+        $messages = $result->getMessages();
+        $message = count($messages) === 1 ? $messages[0] : $result->getSummary();
+        $this->messenger()->addError($message);
+      }
+    }
     return $form;
   }
 
@@ -256,4 +273,19 @@ class UpdaterForm extends FormBase {
     batch_set($batch);
   }
 
+  /**
+   * Gets validation errors before an update begins.
+   *
+   * @param string $update_version
+   *   The version of Drupal to which we will update.
+   *
+   * @return \Drupal\package_manager\ValidationResult[]
+   *   The error validation results.
+   */
+  private function getReadinessErrors(string $update_version): array {
+    $event = new ReadinessCheckEvent($this->updater, ['drupal' => $update_version]);
+    $this->eventDispatcher->dispatch($event);
+    return $event->getResults(SystemManager::REQUIREMENT_ERROR);
+  }
+
 }
diff --git a/src/Validation/AdminReadinessMessages.php b/src/Validation/AdminReadinessMessages.php
index 6dd6b10c28ae894d4aed5ad4969d69a654c8856b..8f07e13353c8b8731ee76d4b110734394d0d7f90 100644
--- a/src/Validation/AdminReadinessMessages.php
+++ b/src/Validation/AdminReadinessMessages.php
@@ -140,6 +140,9 @@ final class AdminReadinessMessages implements ContainerInjectionInterface {
         'update.settings',
         'system.status',
         'update.confirmation_page',
+        'automatic_updates.report_update',
+        'automatic_updates.module_update',
+        'automatic_updates.theme_update',
       ];
       return !in_array($this->currentRouteMatch->getRouteName(), $disabled_routes, TRUE);
     }
diff --git a/src/Validation/ReadinessValidationManager.php b/src/Validation/ReadinessValidationManager.php
index 158c3ea6dc1e03f74077c4e97780475f6e6abefa..0a09a3034f38f804ea761249657e95fde7a54234 100644
--- a/src/Validation/ReadinessValidationManager.php
+++ b/src/Validation/ReadinessValidationManager.php
@@ -2,10 +2,12 @@
 
 namespace Drupal\automatic_updates\Validation;
 
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
 use Drupal\automatic_updates\Updater;
 use Drupal\automatic_updates\UpdateRecommender;
 use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
@@ -50,6 +52,20 @@ class ReadinessValidationManager {
    */
   protected $updater;
 
+  /**
+   * The cron updater service.
+   *
+   * @var \Drupal\automatic_updates\CronUpdater
+   */
+  protected $cronUpdater;
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $config;
+
   /**
    * Constructs a ReadinessValidationManager.
    *
@@ -61,14 +77,20 @@ class ReadinessValidationManager {
    *   The event dispatcher service.
    * @param \Drupal\automatic_updates\Updater $updater
    *   The updater service.
+   * @param \Drupal\automatic_updates\CronUpdater $cron_updater
+   *   The cron updater service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
+   *   The config factory service.
    * @param int $results_time_to_live
    *   The number of hours to store results.
    */
-  public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, EventDispatcherInterface $dispatcher, Updater $updater, int $results_time_to_live) {
+  public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, EventDispatcherInterface $dispatcher, Updater $updater, CronUpdater $cron_updater, ConfigFactoryInterface $config, int $results_time_to_live) {
     $this->keyValueExpirable = $key_value_expirable_factory->get('automatic_updates');
     $this->time = $time;
     $this->eventDispatcher = $dispatcher;
     $this->updater = $updater;
+    $this->cronUpdater = $cron_updater;
+    $this->config = $config;
     $this->resultsTimeToLive = $results_time_to_live;
   }
 
@@ -78,20 +100,20 @@ class ReadinessValidationManager {
    * @return $this
    */
   public function run(): self {
-    $composer = $this->updater->getActiveComposer();
-
     $recommender = new UpdateRecommender();
     $release = $recommender->getRecommendedRelease(TRUE);
-    if ($release) {
-      $core_packages = $composer->getCorePackageNames();
-      // Update all core packages to the same version.
-      $package_versions = array_fill(0, count($core_packages), $release->getVersion());
-      $package_versions = array_combine($core_packages, $package_versions);
+    // If updates will run during cron, use the cron updater service provided by
+    // this module. This will allow subscribers to ReadinessCheckEvent to run
+    // specific validation for conditions that only affect cron updates.
+    if ($this->config->get('automatic_updates.settings')->get('cron') == CronUpdater::DISABLED) {
+      $stage = $this->updater;
     }
     else {
-      $package_versions = [];
+      $stage = $this->cronUpdater;
     }
-    $event = new ReadinessCheckEvent($this->updater, $package_versions);
+
+    $project_versions = $release ? ['drupal' => $release->getVersion()] : [];
+    $event = new ReadinessCheckEvent($stage, $project_versions);
     $this->eventDispatcher->dispatch($event);
     $results = $event->getResults();
     $this->keyValueExpirable->setWithExpire(
@@ -116,10 +138,16 @@ class ReadinessValidationManager {
     $listeners = $this->eventDispatcher->getListeners($event_name);
     $string = '';
     foreach ($listeners as $listener) {
-      /** @var object $object */
-      $object = $listener[0];
-      $method = $listener[1];
-      $string .= '-' . get_class($object) . '::' . $method;
+      if (is_array($listener)) {
+        $string .= is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
+        $string .= $listener[1];
+      }
+      elseif (is_object($listener)) {
+        $string .= "-" . get_class($listener);
+      }
+      elseif (is_string($listener)) {
+        $string .= "-$listener";
+      }
     }
     return $string;
   }
diff --git a/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php b/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
index c45e4a11ddd207b44c7789fa9eee1c6b340baeeb..ced48e45b18277661cc033326927252831e7fca7 100644
--- a/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
+++ b/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation;
 
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\Event\ReadinessCheckEvent;
+use Drupal\automatic_updates\Updater;
 use Drupal\automatic_updates_test\ReadinessChecker\TestChecker1;
 use Drupal\automatic_updates_test2\ReadinessChecker\TestChecker2;
 use Drupal\system\SystemManager;
@@ -210,4 +212,25 @@ class ReadinessValidationManagerTest extends AutomaticUpdatesKernelTestBase {
     $this->assertCheckerResultsFromManager($expected_results);
   }
 
+  /**
+   * Tests the Automatic Updates cron setting changes which stage class is used.
+   */
+  public function testCronSetting(): void {
+    $this->enableModules(['automatic_updates']);
+    $stage_class = NULL;
+    $listener = function (ReadinessCheckEvent $event) use (&$stage_class): void {
+      $stage_class = get_class($event->getStage());
+    };
+    $event_dispatcher = $this->container->get('event_dispatcher');
+    $event_dispatcher->addListener(ReadinessCheckEvent::class, $listener);
+    $this->container->get('automatic_updates.readiness_validation_manager')->run();
+    // By default, updates will be enabled on cron.
+    $this->assertSame(CronUpdater::class, $stage_class);
+    $this->config('automatic_updates.settings')
+      ->set('cron', CronUpdater::DISABLED)
+      ->save();
+    $this->container->get('automatic_updates.readiness_validation_manager')->run();
+    $this->assertSame(Updater::class, $stage_class);
+  }
+
 }