diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index ce546a1feb02a176539052c5c3c5565153b16d90..67437b6e003348a5194f6bb63d696bdfd65137cf 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -129,3 +129,7 @@ services:
       - '@string_translation'
     tags:
       - { name: event_subscriber }
+  automatic_updates.validator.target_release:
+    class: \Drupal\automatic_updates\Validator\UpdateReleaseValidator
+    tags:
+      - { name: event_subscriber }
diff --git a/src/Validator/UpdateReleaseValidator.php b/src/Validator/UpdateReleaseValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..d501d1cda78fad7d7026eb4880f39e2b181fc559
--- /dev/null
+++ b/src/Validator/UpdateReleaseValidator.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\automatic_updates\Validator;
+
+use Drupal\automatic_updates\ProjectInfo;
+use Drupal\automatic_updates\Updater;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Validates that the target release of Drupal core is secure and supported.
+ */
+class UpdateReleaseValidator implements EventSubscriberInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * Checks that the target version of Drupal core is secure and supported.
+   *
+   * @param \Drupal\package_manager\Event\PreCreateEvent $event
+   *   The event object.
+   */
+  public function checkRelease(PreCreateEvent $event): void {
+    $stage = $event->getStage();
+    // This check only works with Automatic Updates.
+    if (!$stage instanceof Updater) {
+      return;
+    }
+
+    $package_versions = $stage->getPackageVersions();
+    // The updater will only update Drupal core, so all production dependencies
+    // will be Drupal core packages.
+    $target_version = reset($package_versions['production']);
+
+    // If the target version isn't in the list of installable releases, then it
+    // isn't secure and supported and we should flag an error.
+    $releases = (new ProjectInfo('drupal'))->getInstallableReleases();
+    if (empty($releases) || !array_key_exists($target_version, $releases)) {
+      $message = $this->t('Cannot update Drupal core to @target_version because it is not in the list of installable releases.', [
+        '@target_version' => $target_version,
+      ]);
+      $event->addError([$message]);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return [
+      PreCreateEvent::class => 'checkRelease',
+    ];
+  }
+
+}
diff --git a/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php b/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
index c9a09cb821e3ad6640a967a3f9f73cb9563790cb..add1626a4ed1933f19f5830a50b7727d71d02efd 100644
--- a/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
+++ b/tests/src/Kernel/ReadinessValidation/ReadinessValidationManagerTest.php
@@ -219,12 +219,8 @@ class ReadinessValidationManagerTest extends AutomaticUpdatesKernelTestBase {
    * Tests that stored validation results are deleted after an update.
    */
   public function testStoredResultsDeletedPostApply(): void {
-    $this->container->get('module_installer')
-      ->install(['automatic_updates']);
-
-    // Ensure there's a simulated core release to update to.
+    $this->enableModules(['automatic_updates']);
     $this->setCoreVersion('9.8.1');
-    $this->setReleaseMetadata(__DIR__ . '/../../../fixtures/release-history/drupal.9.8.2.xml');
 
     // The readiness checker should raise a warning, so that the update is not
     // blocked or aborted.
diff --git a/tests/src/Kernel/ReadinessValidation/SettingsValidatorTest.php b/tests/src/Kernel/ReadinessValidation/SettingsValidatorTest.php
index 30493cecf7d6ed2a66c349ff52761a86186a7057..dff44ec010b88f503058d7f0682c3cb2fa574481 100644
--- a/tests/src/Kernel/ReadinessValidation/SettingsValidatorTest.php
+++ b/tests/src/Kernel/ReadinessValidation/SettingsValidatorTest.php
@@ -51,7 +51,7 @@ class SettingsValidatorTest extends AutomaticUpdatesKernelTestBase {
     $this->assertCheckerResultsFromManager($expected_results, TRUE);
     try {
       $this->container->get('automatic_updates.updater')->begin([
-        'drupal' => '9.8.1',
+        'drupal' => '9.8.2',
       ]);
       // If there was no exception, ensure we're not expecting any errors.
       $this->assertSame([], $expected_results);
diff --git a/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
index edc1146f48ae9a24a61fe788442204d72c0d0e6f..eef8b02e2e5884b81a6e2124327f09ba05836a4e 100644
--- a/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
+++ b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
@@ -61,7 +61,7 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase {
     }
 
     $updater = $this->container->get('automatic_updates.updater');
-    $stage_id = $updater->begin(['drupal' => '9.8.1']);
+    $stage_id = $updater->begin(['drupal' => '9.8.2']);
     if ($stage_dir_exists) {
       // Copy the fixture's staging directory into a subdirectory using the
       // stage ID as the directory name.
diff --git a/tests/src/Kernel/ReadinessValidation/UpdateReleaseValidatorTest.php b/tests/src/Kernel/ReadinessValidation/UpdateReleaseValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4234d103dcdedd0793cbb05104ed6d34e8bdc908
--- /dev/null
+++ b/tests/src/Kernel/ReadinessValidation/UpdateReleaseValidatorTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation;
+
+use Drupal\automatic_updates\Exception\UpdateException;
+use Drupal\package_manager\ValidationResult;
+use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
+
+/**
+ * @covers \Drupal\automatic_updates\Validator\UpdateReleaseValidator
+ *
+ * @group automatic_updates
+ */
+class UpdateReleaseValidatorTest extends AutomaticUpdatesKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['automatic_updates'];
+
+  /**
+   * Tests that an error is raised when trying to update to an unknown release.
+   */
+  public function testUnknownReleaseRaisesError(): void {
+    $result = ValidationResult::createError([
+      'Cannot update Drupal core to 9.8.99 because it is not in the list of installable releases.',
+    ]);
+
+    try {
+      $this->container->get('automatic_updates.updater')->begin([
+        'drupal' => '9.8.99',
+      ]);
+      $this->fail('Expected an exception to be thrown, but it was not.');
+    }
+    catch (UpdateException $e) {
+      $this->assertValidationResultsEqual([$result], $e->getResults());
+    }
+  }
+
+}
diff --git a/tests/src/Kernel/UpdaterTest.php b/tests/src/Kernel/UpdaterTest.php
index f48cb59281010166b86813aa2d3d7b3e7f536647..8dbaeaafb620c27f283bbfd8322be322206c41ce 100644
--- a/tests/src/Kernel/UpdaterTest.php
+++ b/tests/src/Kernel/UpdaterTest.php
@@ -34,6 +34,9 @@ class UpdaterTest extends AutomaticUpdatesKernelTestBase {
    * Tests that correct versions are staged after calling ::begin().
    */
   public function testCorrectVersionsStaged(): void {
+    // Simulate that we're running Drupal 9.8.0 and a security update to 9.8.1
+    // is available.
+    $this->setCoreVersion('9.8.0');
     $this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/drupal.9.8.1-security.xml');
 
     // Create a user who will own the stage even after the container is rebuilt.