diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index fd68d501ae8f89d420d193d441272037da003495..51b7a36f9cd83065400ab8fbd463e95654b7393f 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -198,6 +198,10 @@ filter:
     id:
       type: string
       label: 'ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.filter
+          interface: 'Drupal\filter\Plugin\FilterInterface'
     provider:
       type: string
       label: 'Provider'
@@ -425,6 +429,10 @@ condition.plugin:
     id:
       type: string
       label: 'ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.condition
+          interface: 'Drupal\Core\Condition\ConditionInterface'
     negate:
       type: boolean
       label: 'Negate'
@@ -527,6 +535,10 @@ field_config_base:
     field_type:
       type: string
       label: 'Field type'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.field.field_type
+          interface: '\Drupal\Core\Field\FieldItemInterface'
 
 core.base_field_override.*.*.*:
   type: field_config_base
@@ -711,6 +723,13 @@ field.field_settings.entity_reference:
     handler:
       type: string
       label: 'Reference method'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.entity_reference_selection
+          interface: 'Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface'
+          # @todo Remove this line and explicitly require valid entity reference
+          # selection plugin IDs in https://drupal.org/i/3420198.
+          allowFallback: true
     handler_settings:
       type: entity_reference_selection.[%parent.handler]
       label: 'Entity reference selection plugin settings'
diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index fa7114d3049941d9ba5fa954642515ce429f7263..4b1cfb701d6a7bcbd518f000021715160653bc0d 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -78,6 +78,10 @@ field_formatter:
     type:
       type: string
       label: 'Format type machine name'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.field.formatter
+          interface: 'Drupal\Core\Field\FormatterInterface'
     label:
       type: string
       label: 'Label setting machine name'
@@ -135,6 +139,10 @@ core.entity_form_display.*.*.*:
           type:
             type: string
             label: 'Widget type machine name'
+            constraints:
+              PluginExists:
+                manager: plugin.manager.field.widget
+                interface: '\Drupal\Core\Field\WidgetInterface'
           weight:
             type: integer
             label: 'Weight'
diff --git a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
index b2baceedb7cadedb36b8d765fb9f7b523dbc152a..f0ccf4f8735b856bd15f753c8b870b4f4ef5d794 100644
--- a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
+++ b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
@@ -48,6 +48,13 @@ class PluginExistsConstraint extends Constraint implements ContainerFactoryPlugi
    */
   public ?string $interface = NULL;
 
+  /**
+   * Whether or not to consider fallback plugin IDs as valid.
+   *
+   * @var bool
+   */
+  public bool $allowFallback = FALSE;
+
   /**
    * Constructs a PluginExistsConstraint.
    *
diff --git a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraintValidator.php
index b862bc339dfee8323b1609382907ae20f7a87be0..ac11a43a78deb65d81f6fa47e4e0eb43a63bf048 100644
--- a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraintValidator.php
+++ b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraintValidator.php
@@ -25,8 +25,11 @@ public function validate(mixed $plugin_id, Constraint $constraint) {
     }
 
     $definition = $constraint->pluginManager->getDefinition($plugin_id, FALSE);
-    // Some plugin managers provide fallbacks.
-    if ($constraint->pluginManager instanceof FallbackPluginManagerInterface) {
+    // Some plugin managers provide fallbacks. In most cases, the use of a
+    // fallback plugin ID suggests that the given plugin ID is invalid in some
+    // way, so by default, we don't consider fallback plugin IDs as valid,
+    // although that can be overridden by the `allowFallback` option if needed.
+    if ($constraint->pluginManager instanceof FallbackPluginManagerInterface && $constraint->allowFallback) {
       $fallback_plugin_id = $constraint->pluginManager->getFallbackPluginId($plugin_id);
       $definition = $constraint->pluginManager->getDefinition($fallback_plugin_id, FALSE);
     }
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index 8989c1d79efe0f6bb3fbe90b04c1fba8db4e327f..f82c157ebc863c17f4de31b34c14109d511605f3 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -33,6 +33,10 @@ block.block.*:
         PluginExists:
           manager: plugin.manager.block
           interface: Drupal\Core\Block\BlockPluginInterface
+          # Block plugin IDs may not be valid in blocks that are backed by
+          # block_content entities that don't exist yet. Therefore, it's okay
+          # to consider the fallback plugin ID as valid.
+          allowFallback: true
     settings:
       type: block.settings.[%parent.plugin]
     visibility:
diff --git a/core/modules/comment/config/optional/views.view.comments_recent.yml b/core/modules/comment/config/optional/views.view.comments_recent.yml
index 5e15280193612352835d062a9cb199047afe7eba..7c4d8c631f53cac17f3947084b1cb35f364ae4c6 100644
--- a/core/modules/comment/config/optional/views.view.comments_recent.yml
+++ b/core/modules/comment/config/optional/views.view.comments_recent.yml
@@ -182,7 +182,7 @@ display:
           admin_label: ''
           entity_type: comment
           entity_field: cid
-          plugin_id: field
+          plugin_id: standard
           order: DESC
           expose:
             label: ''
diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml
index ca182c42abf929852b67feaaedb24ec7ca70eb37..a7ec988b1af90de164c5dfec8007edf8542d56e7 100644
--- a/core/modules/field/config/schema/field.schema.yml
+++ b/core/modules/field/config/schema/field.schema.yml
@@ -24,6 +24,10 @@ field.storage.*.*:
     type:
       type: string
       label: 'Type'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.field.field_type
+          interface: '\Drupal\Core\Field\FieldItemInterface'
     settings:
       type: field.storage_settings.[%parent.type]
     module:
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index f6b2dbf8144879178ba1ecfca676522dea9bc0e1..34d590bbd2ca2ffb4c9a7ec1016621bc3a645ec2 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -225,3 +225,9 @@ function field_test_field_info_entity_type_ui_definitions_alter(array &$ui_defin
     $ui_definitions['boolean']['label'] = new TranslatableMarkup('Boolean (overridden by alter)');
   }
 }
+
+function field_test_entity_reference_selection_alter(array &$definitions): void {
+  if (\Drupal::state()->get('field_test_disable_broken_entity_reference_handler')) {
+    unset($definitions['broken']);
+  }
+}
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldWidget/TestFieldWidget.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldWidget/TestFieldWidget.php
index ef4efd2078cbd88e052d1ec4e290c3f0317460f3..7447623cbdc82d47dc28e80eafb0b2429023cc46 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldWidget/TestFieldWidget.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldWidget/TestFieldWidget.php
@@ -14,6 +14,7 @@
  *   id = "test_field_widget",
  *   label = @Translation("Test widget"),
  *   field_types = {
+ *     "field_test",
  *     "test_field",
  *     "hidden_test_field",
  *     "test_field_with_preconfigured_options"
diff --git a/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php b/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
index 2378e1308567ed2c11f09ece46d8f9295f61b801..45a7b787412f869bb3b2634af6344621cfb6cd7a 100644
--- a/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
+++ b/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
@@ -120,6 +120,7 @@ public function testImmutableProperties(array $valid_values = []): void {
     parent::testImmutableProperties([
       'entity_type' => 'entity_test_with_bundle',
       'bundle' => 'another',
+      'field_type' => 'string',
     ]);
   }
 
@@ -149,4 +150,40 @@ public function testRequiredPropertyValuesMissing(?array $additional_expected_va
     ]);
   }
 
+  /**
+   * Tests that the field type plugin's existence is validated.
+   */
+  public function testFieldTypePluginIsValidated(): void {
+    // The `field_type` property is immutable, so we need to clone the entity in
+    // order to cleanly change its immutable properties.
+    $this->entity = $this->entity->createDuplicate()
+      // We need to clear the current settings, or we will get validation errors
+      // because the old settings are not supported by the new field type.
+      ->set('settings', [])
+      ->set('field_type', 'invalid');
+
+    $this->assertValidationErrors([
+      'field_type' => "The 'invalid' plugin does not exist.",
+    ]);
+  }
+
+  /**
+   * Tests that entity reference selection handler plugin IDs are validated.
+   */
+  public function testEntityReferenceSelectionHandlerIsValidated(): void {
+    $this->container->get('state')
+      ->set('field_test_disable_broken_entity_reference_handler', TRUE);
+    $this->enableModules(['field_test']);
+
+    // The `field_type` property is immutable, so we need to clone the entity in
+    // order to cleanly change its immutable properties.
+    $this->entity = $this->entity->createDuplicate()
+      ->set('field_type', 'entity_reference')
+      ->set('settings', ['handler' => 'non_existent']);
+
+    $this->assertValidationErrors([
+      'settings.handler' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php b/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
index 83e0e72d50306693a28828d46fc937d404690ff3..5813bc7a5a39b0052c4cf1769e8e324e6b287dd1 100644
--- a/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
+++ b/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
@@ -33,4 +33,26 @@ protected function setUp(): void {
     $this->entity->save();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function testImmutableProperties(array $valid_values = []): void {
+    $valid_values['type'] = 'string';
+    parent::testImmutableProperties($valid_values);
+  }
+
+  /**
+   * Tests that the field type plugin's existence is validated.
+   */
+  public function testFieldTypePluginIsValidated(): void {
+    // The `type` property is immutable, so we need to clone the entity in
+    // order to cleanly change its immutable properties.
+    $this->entity = $this->entity->createDuplicate()
+      ->set('type', 'invalid');
+
+    $this->assertValidationErrors([
+      'type' => "The 'invalid' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php b/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php
index 2d0b9b6e507535890f2fdf0fc4cc7b911aa8cca8..c5c473b4ad1b80107f988fb6776883fa552321e1 100644
--- a/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php
+++ b/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php
@@ -19,6 +19,7 @@ class FieldLayoutEntityDisplayTest extends KernelTestBase {
     'field_layout',
     'entity_test',
     'field_layout_test',
+    'field_test',
     'system',
   ];
 
@@ -34,7 +35,7 @@ public function testPreSave() {
       'mode' => 'default',
       'status' => TRUE,
       'content' => [
-        'foo' => ['type' => 'visible'],
+        'foo' => ['type' => 'field_no_settings'],
         'name' => ['type' => 'hidden', 'region' => 'content'],
       ],
       'hidden' => [
@@ -60,7 +61,7 @@ public function testPreSave() {
       'mode' => 'default',
       'content' => [
         'foo' => [
-          'type' => 'visible',
+          'type' => 'field_no_settings',
         ],
       ],
       'hidden' => [
@@ -99,7 +100,7 @@ public function testPreSave() {
     ];
     // The field was moved to the default region.
     $expected['content']['foo'] = [
-      'type' => 'visible',
+      'type' => 'field_no_settings',
       'region' => 'main',
       'weight' => -4,
       'settings' => [],
diff --git a/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php b/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php
index a2fb83175b324aff13f2f419918c528057ea9410..e5e1a40fb057c2cbecea502a3407addd9e588867 100644
--- a/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php
+++ b/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php
@@ -47,7 +47,9 @@ protected function setUp(): void {
 
       \Drupal::service('entity_display.repository')
         ->getFormDisplay($entity_type, $entity_type)
-        ->setComponent('field_test_no_plugin', [])
+        ->setComponent('field_test_no_plugin', [
+          'type' => 'test_field_widget',
+        ])
         ->save();
     }
 
diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml
index d3c9e980f0cf0fa95f11d7c723082c4f78078d86..7855f536702250153fd6fa7ef60621b402b77320 100644
--- a/core/modules/image/config/schema/image.schema.yml
+++ b/core/modules/image/config/schema/image.schema.yml
@@ -18,6 +18,10 @@ image.style.*:
             type: uuid
           id:
             type: string
+            constraints:
+              PluginExists:
+                manager: plugin.manager.image.effect
+                interface: 'Drupal\image\ImageEffectInterface'
           weight:
             type: integer
           data:
diff --git a/core/modules/layout_builder/config/schema/layout_builder.schema.yml b/core/modules/layout_builder/config/schema/layout_builder.schema.yml
index 7bc4461891b5889f1ce25d63b1ebd91c035bd546..0786286f1f9cae4276a94d1194552754789f02dd 100644
--- a/core/modules/layout_builder/config/schema/layout_builder.schema.yml
+++ b/core/modules/layout_builder/config/schema/layout_builder.schema.yml
@@ -20,6 +20,10 @@ layout_builder.section:
     layout_id:
       type: string
       label: 'Layout ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.core.layout
+          interface: '\Drupal\Core\Layout\LayoutInterface'
     layout_settings:
       type: layout_plugin.settings.[%parent.layout_id]
       label: 'Layout settings'
diff --git a/core/modules/media/config/schema/media.schema.yml b/core/modules/media/config/schema/media.schema.yml
index 1e1bfb5ad3fd442665d05980514ae0949a6a7263..4311b11049c688520e4a1efd086f1ba3e398498c 100644
--- a/core/modules/media/config/schema/media.schema.yml
+++ b/core/modules/media/config/schema/media.schema.yml
@@ -37,6 +37,10 @@ media.type.*:
     source:
       type: string
       label: 'Source'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.media.source
+          interface: 'Drupal\media\MediaSourceInterface'
     queue_thumbnail_downloads:
       type: boolean
       label: 'Whether the thumbnail downloads should be queued'
diff --git a/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php b/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
index 4f8ba575a529788fac72d3d85b7c5649e0ed566a..cd4f46964358685353220b4f4470e4918db718f9 100644
--- a/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
@@ -36,7 +36,28 @@ public function testImmutableProperties(array $valid_values = []): void {
     // settings from the *old* source won't match the config schema for the
     // settings of the *new* source.
     $this->entity->set('source_configuration', []);
+    $valid_values['source'] = 'image';
     parent::testImmutableProperties($valid_values);
   }
 
+  /**
+   * Tests that the media source plugin's existence is validated.
+   */
+  public function testMediaSourceIsValidated(): void {
+    // The `source` property is immutable, so we need to clone the entity in
+    // order to cleanly change its immutable properties.
+    $this->entity = $this->entity->createDuplicate()
+      // The `id` property is thrown out by createDuplicate().
+      ->set('id', 'test')
+      // We need to clear the current source configuration, or we will get
+      // validation errors because the old configuration is not supported by the
+      // new source.
+      ->set('source_configuration', [])
+      ->set('source', 'invalid');
+
+    $this->assertValidationErrors([
+      'source' => "The 'invalid' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/node/config/optional/views.view.frontpage.yml b/core/modules/node/config/optional/views.view.frontpage.yml
index e5e5bd6d9f7dd1342bde887c5cbd083105f1ffe6..8a46368b1bfc8266df450b4df93b04906278ded8 100644
--- a/core/modules/node/config/optional/views.view.frontpage.yml
+++ b/core/modules/node/config/optional/views.view.frontpage.yml
@@ -107,7 +107,7 @@ display:
           admin_label: ''
           entity_type: node
           entity_field: sticky
-          plugin_id: boolean
+          plugin_id: standard
           order: DESC
           expose:
             label: ''
diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml
index 467ae5b6dc2ce397e152cbf9ada39b80d7ab7002..f7b3b8a8e7e0eeb70d53ca8253c2fbc8b6909e44 100644
--- a/core/modules/rest/config/schema/rest.schema.yml
+++ b/core/modules/rest/config/schema/rest.schema.yml
@@ -79,6 +79,10 @@ rest.resource.*:
     plugin_id:
       type: string
       label: 'REST resource plugin id'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.rest
+          interface: 'Drupal\rest\Plugin\ResourceInterface'
     granularity:
       type: string
       label: 'REST resource configuration granularity'
diff --git a/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php b/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
index 1859afa5dd5660388f52aace228ce875a8787005..4660ef802ed4003f1199acad93db28261bf31c81 100644
--- a/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
+++ b/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
@@ -38,4 +38,14 @@ protected function setUp(): void {
     $this->entity->save();
   }
 
+  /**
+   * Tests that the resource plugin ID is validated.
+   */
+  public function testInvalidPluginId(): void {
+    $this->entity->set('plugin_id', 'non_existent');
+    $this->assertValidationErrors([
+      'plugin_id' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/search/config/schema/search.schema.yml b/core/modules/search/config/schema/search.schema.yml
index 9c7a44cd6113e621c06536c0d25dd00ec4cd2af4..f4284a0e12d5b394ae05e5b52f92161b2425411f 100644
--- a/core/modules/search/config/schema/search.schema.yml
+++ b/core/modules/search/config/schema/search.schema.yml
@@ -86,6 +86,10 @@ search.page.*:
     plugin:
       type: string
       label: 'Plugin'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.search
+          interface: 'Drupal\search\Plugin\SearchInterface'
     configuration:
       type: search.plugin.[%parent.plugin]
 
diff --git a/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php b/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
index eb35264fd3f1b373aa8c43501f75726d92521187..9dd113166404e7d51b2fbc600e2a937db93db0d0 100644
--- a/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
+++ b/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
@@ -31,4 +31,14 @@ protected function setUp(): void {
     $this->entity->save();
   }
 
+  /**
+   * Tests that the search plugin ID is validated.
+   */
+  public function testInvalidPluginId(): void {
+    $this->entity->set('plugin', 'non_existent');
+    $this->assertValidationErrors([
+      'plugin' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 504acac218e62dcce68ca203446bfd11ca46aafe..d9f36cc507f9c6e68ddd66021b95549b2616ed0a 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -265,6 +265,10 @@ system.action.*:
     plugin:
       type: string
       label: 'Plugin'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.action
+          interface: 'Drupal\Core\Action\ActionInterface'
     configuration:
       type: action.configuration.[%parent.plugin]
 
@@ -316,6 +320,10 @@ system.mail:
       sequence:
         type: string
         label: 'Interface'
+        constraints:
+          PluginExists:
+            manager: plugin.manager.mail
+            interface: 'Drupal\Core\Mail\MailInterface'
     mailer_dsn:
       type: mapping
       label: 'Symfony mailer transport DSN'
diff --git a/core/modules/system/tests/src/Kernel/Entity/ActionValidationTest.php b/core/modules/system/tests/src/Kernel/Entity/ActionValidationTest.php
index eff928e210a611391ab11a9af5985fff27103ae4..4b034c82306ad37fa54b67067c9f6aee26f43723 100644
--- a/core/modules/system/tests/src/Kernel/Entity/ActionValidationTest.php
+++ b/core/modules/system/tests/src/Kernel/Entity/ActionValidationTest.php
@@ -41,4 +41,14 @@ public function providerInvalidMachineNameCharacters(): array {
     return $cases;
   }
 
+  /**
+   * Tests that the action plugin ID is validated.
+   */
+  public function testInvalidPluginId(): void {
+    $this->entity->set('plugin', 'non_existent');
+    $this->assertValidationErrors([
+      'plugin' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/tour/config/schema/tour.schema.yml b/core/modules/tour/config/schema/tour.schema.yml
index 898198b27be13c48436c744ea147f8a2025bb651..27cbcf7925a9299149bdcc6f4e96e4af992e49cd 100644
--- a/core/modules/tour/config/schema/tour.schema.yml
+++ b/core/modules/tour/config/schema/tour.schema.yml
@@ -41,6 +41,10 @@ tour.tip:
     plugin:
       type: string
       label: 'Plugin'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.tour.tip
+          interface: '\Drupal\tour\TipPluginInterface'
     label:
       type: required_label
       label: 'Label'
diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml
index 03f13c5fc6c2527b1321b9a3da8c7fbbf9209af7..3495ce93fa2bea0bec237620156df2c81404ee59 100644
--- a/core/modules/views/config/schema/views.data_types.schema.yml
+++ b/core/modules/views/config/schema/views.data_types.schema.yml
@@ -25,6 +25,10 @@ views_display:
         type:
           type: string
           label: 'Pager type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.pager
+              interface: 'Drupal\views\Plugin\views\pager\PagerPluginBase'
         options:
           type: views.pager.[%parent.type]
     exposed_form:
@@ -34,6 +38,10 @@ views_display:
         type:
           type: string
           label: 'Exposed form type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.exposed_form
+              interface: 'Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface'
         options:
           label: 'Options'
           type: views.exposed_form.[%parent.type]
@@ -44,6 +52,9 @@ views_display:
         type:
           type: string
           label: 'Access type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.access
         options:
           type: views.access.[%parent.type]
     cache:
@@ -88,6 +99,10 @@ views_display:
         type:
           type: string
           label: 'Type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.style
+              interface: 'Drupal\views\Plugin\views\style\StylePluginBase'
         options:
           type: views.style.[%parent.type]
     row:
@@ -97,6 +112,9 @@ views_display:
         type:
           type: string
           label: 'Row type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.row
         options:
           type: views.row.[%parent.type]
     query:
@@ -106,6 +124,9 @@ views_display:
         type:
           type: string
           label: 'Query type'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.query
         options:
           type: views.query.[%parent.type]
     defaults:
@@ -274,6 +295,12 @@ views_sort:
     plugin_id:
       type: string
       label: 'Plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.sort
+          # @todo Remove this line and fix all views in core which use invalid
+          # sort plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_sort_expose:
   type: mapping
@@ -298,6 +325,12 @@ views_area:
     plugin_id:
       type: string
       label: 'Plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.area
+          # @todo Remove this line and fix all views in core which use invalid
+          # area plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_handler:
   type: mapping
@@ -359,6 +392,9 @@ views_argument:
     default_argument_type:
       type: string
       label: 'Type'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.argument_default
     default_argument_options:
       type: views.argument_default.[%parent.default_argument_type]
       label: 'Default argument options'
@@ -388,6 +424,9 @@ views_argument:
         type:
           type: string
           label: 'Validator'
+          constraints:
+            PluginExists:
+              manager: plugin.manager.views.argument_validator
         fail:
           type: string
           label: 'Action to take if filter value does not validate'
@@ -415,6 +454,12 @@ views_argument:
     plugin_id:
       type: string
       label: 'Plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.argument
+          # @todo Remove this line and fix all views in core which use invalid
+          # argument plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_exposed_form:
   type: mapping
@@ -574,6 +619,12 @@ views_field:
     plugin_id:
       type: string
       label: 'Plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.field
+          # @todo Remove this line and fix all views in core which use invalid
+          # field plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_pager:
   type: mapping
@@ -780,6 +831,12 @@ views_filter:
     plugin_id:
       type: string
       label: 'Plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.filter
+          # @todo Remove this line and fix all views in core which use invalid
+          # filter plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_filter_group_item:
   type: mapping
@@ -804,6 +861,15 @@ views_relationship:
     required:
       type: boolean
       label: 'Require this relationship'
+    plugin_id:
+      type: string
+      label: 'The plugin ID'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.relationship
+          # @todo Remove this line and fix all views in core which use invalid
+          # relationship plugins in https://drupal.org/i/3387325.
+          allowFallback: true
 
 views_query:
   type: mapping
@@ -831,6 +897,9 @@ views_cache:
     type:
       type: string
       label: 'Cache type'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.views.cache
 
 views_display_extender:
   type: mapping
diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml
index c279ee0d23f4622479a4eabe2b65c85f6d59fec8..8ddd73931f2c2d2c3ee2ed35efdddbf141daa5b8 100644
--- a/core/modules/views/config/schema/views.schema.yml
+++ b/core/modules/views/config/schema/views.schema.yml
@@ -108,6 +108,9 @@ views.view.*:
           display_plugin:
             type: string
             label: 'Display plugin'
+            constraints:
+              PluginExists:
+                manager: plugin.manager.views.display
           position:
             type: integer
             label: 'Position'
diff --git a/core/modules/views/tests/modules/views_test_config/config/schema/views_test_config.views.schema.yml b/core/modules/views/tests/modules/views_test_config/config/schema/views_test_config.views.schema.yml
index 199cdfcbb74f99a7da920110f30b536e84e7a530..d214c19cc22a5ad13d7194be6ef91b8dbe5733ec 100644
--- a/core/modules/views/tests/modules/views_test_config/config/schema/views_test_config.views.schema.yml
+++ b/core/modules/views/tests/modules/views_test_config/config/schema/views_test_config.views.schema.yml
@@ -10,3 +10,6 @@ views.area.test_example:
     custom_access:
       type: boolean
       label: 'Should access to the area be allowed'
+
+views.cache.non_existent:
+  type: views_cache
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
index 67c8ad03b24b5ce872f7e0790eab3d939fbae3bf..d68ce0ebe4b6d373208b8bbbdd9cacdd8074e3b0 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
@@ -35,7 +35,7 @@ display:
           id: name
           relationship: none
           table: views_test_data
-          plugin_id: string
+          plugin_id: standard
       pager:
         options:
           offset: 0
@@ -47,7 +47,7 @@ display:
           order: ASC
           relationship: none
           table: views_test_data
-          plugin_id: numeric
+          plugin_id: standard
     display_plugin: default
     display_title: Default
     id: default
diff --git a/core/modules/views/tests/modules/views_test_config/views_test_config.module b/core/modules/views/tests/modules/views_test_config/views_test_config.module
index 88cbdbcaaf60b30785e81892e54a0d8daff448ae..6884c6e92efc691f7f197915d82ad54af0488c77 100644
--- a/core/modules/views/tests/modules/views_test_config/views_test_config.module
+++ b/core/modules/views/tests/modules/views_test_config/views_test_config.module
@@ -32,3 +32,33 @@ function views_test_config_views_post_render(ViewExecutable $view, &$output, Cac
     $output['#cache']['tags'][] = 'foo';
   }
 }
+
+function _views_test_config_disable_broken_handler(array &$definitions, string $handler_type): void {
+  if (in_array($handler_type, \Drupal::state()->get('views_test_config_disable_broken_handler', []))) {
+    unset($definitions['broken']);
+  }
+}
+
+function views_test_config_views_plugins_area_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'area');
+}
+
+function views_test_config_views_plugins_argument_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'argument');
+}
+
+function views_test_config_views_plugins_field_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'field');
+}
+
+function views_test_config_views_plugins_filter_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'filter');
+}
+
+function views_test_config_views_plugins_relationship_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'relationship');
+}
+
+function views_test_config_views_plugins_sort_alter(array &$definitions): void {
+  _views_test_config_disable_broken_handler($definitions, 'sort');
+}
diff --git a/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php b/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
index 2fe755f98a75202668903a36b428097dc0072dea..70bfc4335d180f693d92190f751a8bef37716875 100644
--- a/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
@@ -30,6 +30,19 @@ class DisplayTest extends ViewTestBase {
    */
   protected static $modules = ['views_ui', 'node', 'block'];
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $configSchemaCheckerExclusions = [
+    // The availability of Views display plugins is validated by the config
+    // system, but one of our test cases saves a view with an invalid display
+    // plugin ID, to see how Views handles that. Therefore, allow that one view
+    // to be saved with an invalid display plugin without angering the config
+    // schema checker.
+    // @see ::testInvalidDisplayPlugins()
+    'views.view.test_display_invalid',
+  ];
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php b/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
index 199b2136a49de29d381fdf525da7a57eecc5387c..d6265bb2a14ac1d4028149e43790f1c61f786a00 100644
--- a/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
+++ b/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\views\Kernel\Entity;
 
+use Drupal\Component\Utility\NestedArray;
 use Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase;
 use Drupal\views\Entity\View;
 
@@ -16,7 +17,7 @@ class ViewValidationTest extends ConfigEntityValidationTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['views'];
+  protected static $modules = ['views', 'views_test_config'];
 
   /**
    * {@inheritdoc}
@@ -40,4 +41,54 @@ public function testLabelsAreRequired(): void {
     $this->assertSame($this->entity->id(), $this->entity->label());
   }
 
+  /**
+   * Tests that the various plugin IDs making up a view display are validated.
+   *
+   * @param string ...$parents
+   *   The array parents of the property of the view's default display which
+   *   will be set to `non_existent`.
+   *
+   * @testWith ["display_plugin"]
+   *   ["display_options", "pager", "type"]
+   *   ["display_options", "exposed_form", "type"]
+   *   ["display_options", "access", "type"]
+   *   ["display_options", "style", "type"]
+   *   ["display_options", "row", "type"]
+   *   ["display_options", "query", "type"]
+   *   ["display_options", "cache", "type"]
+   *   ["display_options", "header", "non_existent", "plugin_id"]
+   *   ["display_options", "footer", "non_existent", "plugin_id"]
+   *   ["display_options", "empty", "non_existent", "plugin_id"]
+   *   ["display_options", "arguments", "non_existent", "plugin_id"]
+   *   ["display_options", "sorts", "non_existent", "plugin_id"]
+   *   ["display_options", "fields", "non_existent", "plugin_id"]
+   *   ["display_options", "filters", "non_existent", "plugin_id"]
+   *   ["display_options", "relationships", "non_existent", "plugin_id"]
+   */
+  public function testInvalidPluginId(string ...$parents): void {
+    // Disable the `broken` handler plugin, which is used as a fallback for
+    // non-existent handler plugins. This ensures that when we use an
+    // invalid handler plugin ID, we will get the expected validation error.
+    // @todo Remove all this when fallback plugin IDs are not longer allowed by
+    //   Views' config schema.
+    // @see views_test_config.module
+    $this->container->get('state')
+      ->set('views_test_config_disable_broken_handler', [
+        'area',
+        'argument',
+        'sort',
+        'field',
+        'filter',
+        'relationship',
+      ]);
+    $this->container->get('plugin.cache_clearer')->clearCachedDefinitions();
+
+    $display = &$this->entity->getDisplay('default');
+    NestedArray::setValue($display, $parents, 'non_existent');
+    $property_path = 'display.default.' . implode('.', $parents);
+    $this->assertValidationErrors([
+      $property_path => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/modules/views/tests/src/Kernel/TestViewsTest.php b/core/modules/views/tests/src/Kernel/TestViewsTest.php
index 14267cb2dd8537b26458c48e2477335ae3e5fd60..fe9c70ffe0b52d863e3afa1d1e22f47079722abe 100644
--- a/core/modules/views/tests/src/Kernel/TestViewsTest.php
+++ b/core/modules/views/tests/src/Kernel/TestViewsTest.php
@@ -29,6 +29,7 @@ class TestViewsTest extends KernelTestBase {
    * @var array
    */
   protected static $modules = [
+    'views',
     // For NodeType config entities to exist, its module must be installed.
     'node',
     // The `DRUPAL_OPTIONAL` constant is used by the NodeType config entity type
@@ -116,6 +117,9 @@ class TestViewsTest extends KernelTestBase {
     // `history` is a module dependency.
     // @see core/modules/views/tests/modules/views_test_config/test_views/views.view.test_history.yml
     'history',
+    // The `image` module is required by at least one of the Node module's
+    // views.
+    'image',
   ];
 
   /**
diff --git a/core/modules/views_ui/tests/src/Functional/OverrideDisplaysTest.php b/core/modules/views_ui/tests/src/Functional/OverrideDisplaysTest.php
index 8208210e051a6553eb6150e5788e4d01857ef576..f27849f6923d97ea68500a8162529c75eb0ed1c8 100644
--- a/core/modules/views_ui/tests/src/Functional/OverrideDisplaysTest.php
+++ b/core/modules/views_ui/tests/src/Functional/OverrideDisplaysTest.php
@@ -67,6 +67,7 @@ public function testOverrideDisplays() {
     $this->assertSession()->pageTextContains($view['label']);
 
     // Place the block.
+    $this->container->get('plugin.manager.block')->clearCachedDefinitions();
     $this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
 
     // Make sure the title appears in the block.
@@ -132,6 +133,7 @@ public function testWizardMixedDefaultOverriddenDisplays() {
     // Put the block into the first sidebar region, and make sure it will not
     // display on the view's page display (since we will be searching for the
     // presence/absence of the view's title in both the page and the block).
+    $this->container->get('plugin.manager.block')->clearCachedDefinitions();
     $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
       'visibility' => [
         'request_path' => [
diff --git a/core/modules/workflows/config/schema/workflows.schema.yml b/core/modules/workflows/config/schema/workflows.schema.yml
index 9c032204d8127020dbd702e740a206b87088daea..1d102d0ac442252652309fb9115fd9e9fd596442 100644
--- a/core/modules/workflows/config/schema/workflows.schema.yml
+++ b/core/modules/workflows/config/schema/workflows.schema.yml
@@ -11,6 +11,10 @@ workflows.workflow.*:
     type:
       type: string
       label: 'Workflow type'
+      constraints:
+        PluginExists:
+          manager: plugin.manager.workflows.type
+          interface: 'Drupal\workflows\WorkflowTypeInterface'
     type_settings:
       type: workflow.type_settings.[%parent.type]
 
diff --git a/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php b/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
index 5f1c558e893fe0b2b494939a62bc8c69fd17c2a9..a840904fe2a7650a3e70a1c3d373588566908352 100644
--- a/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
+++ b/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
@@ -32,4 +32,14 @@ protected function setUp(): void {
     $this->entity->save();
   }
 
+  /**
+   * Tests that the workflow type plugin is validated.
+   */
+  public function testTypePluginIsValidated(): void {
+    $this->entity->set('type', 'non_existent');
+    $this->assertValidationErrors([
+      'type' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
 }
diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml
index bd9f57c3160ffbc4d7d373c937dfe34e6d9b6e63..77aaac869b3aca25019ba89f5c44447d7c3cfc47 100644
--- a/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml
+++ b/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml
@@ -112,12 +112,6 @@ content:
     settings:
       include_locked: true
     third_party_settings: {  }
-  layout_builder__layout:
-    type: null
-    weight: 26
-    region: content
-    settings: {  }
-    third_party_settings: {  }
   moderation_state:
     type: moderation_state_default
     weight: 20
@@ -174,4 +168,5 @@ content:
       size: 60
       placeholder: ''
     third_party_settings: {  }
-hidden: {  }
+hidden:
+  layout_builder__layout: true
diff --git a/core/profiles/demo_umami/config/install/views.view.frontpage.yml b/core/profiles/demo_umami/config/install/views.view.frontpage.yml
index 0c3387aaa6f193fb0f35dbea39551a5321914f20..fbabf2664f69a8d96387502a502ab2a4d54aa8d4 100644
--- a/core/profiles/demo_umami/config/install/views.view.frontpage.yml
+++ b/core/profiles/demo_umami/config/install/views.view.frontpage.yml
@@ -91,7 +91,7 @@ display:
           admin_label: ''
           entity_type: node
           entity_field: sticky
-          plugin_id: boolean
+          plugin_id: standard
           order: DESC
           expose:
             label: ''
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
index c1c6d2fe6135552243b13bfacf479cbcab10ec0e..edef4e1f6c5f15cecbbf32c412d83b02b6496225 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
@@ -11,6 +11,7 @@
 use Drupal\Core\TypedData\Plugin\DataType\StringData;
 use Drupal\Core\TypedData\Type\IntegerInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\image\ImageEffectInterface;
 use Drupal\KernelTests\KernelTestBase;
 
 /**
@@ -198,6 +199,12 @@ public function testSchemaMapping() {
     $expected['mapping']['effects']['type'] = 'sequence';
     $expected['mapping']['effects']['sequence']['type'] = 'mapping';
     $expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string';
+    $expected['mapping']['effects']['sequence']['mapping']['id']['constraints'] = [
+      'PluginExists' => [
+        'manager' => 'plugin.manager.image.effect',
+        'interface' => ImageEffectInterface::class,
+      ],
+    ];
     $expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]';
     $expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer';
     $expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'uuid';
diff --git a/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php b/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
index 5b333b19ffb82b80a62c0c8fc9e19a8f2ed835c7..5da73d743391320ab0ebaa3705e94fb87f3e5b71 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
@@ -149,4 +149,27 @@ public function testSpecialCharacters(string $config_name, string $property, str
     }
   }
 
+  /**
+   * Tests that plugin IDs in simple config are validated.
+   *
+   * @param string $config_name
+   *   The name of the config object to validate.
+   * @param string $property
+   *   The property path to set. This will receive the value 'non_existent' and
+   *   is expected to raise a "plugin does not exist" error.
+   *
+   * @testWith ["system.mail", "interface.0"]
+   */
+  public function testInvalidPluginId(string $config_name, string $property): void {
+    $config = $this->config($config_name);
+
+    $violations = $this->container->get('config.typed')
+      ->createFromNameAndData($config_name, $config->set($property, 'non_existent')->get())
+      ->validate();
+
+    $this->assertCount(1, $violations);
+    $this->assertSame($property, $violations[0]->getPropertyPath());
+    $this->assertSame("The 'non_existent' plugin does not exist.", (string) $violations[0]->getMessage());
+  }
+
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/BaseFieldOverrideValidationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/BaseFieldOverrideValidationTest.php
index bcc2a30396457e943fe2657c0586018f642bd666..124d0368809889c2cc8f0b1954ace86c983857fb 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/BaseFieldOverrideValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/BaseFieldOverrideValidationTest.php
@@ -38,7 +38,7 @@ protected function setUp(): void {
     $fields = $this->container->get('entity_field.manager')
       ->getBaseFieldDefinitions('node');
 
-    $this->entity = BaseFieldOverride::createFromBaseFieldDefinition(reset($fields), 'one');
+    $this->entity = BaseFieldOverride::createFromBaseFieldDefinition($fields['uuid'], 'one');
     $this->entity->save();
   }
 
@@ -65,6 +65,20 @@ public function testImmutableProperties(array $valid_values = []): void {
     parent::testImmutableProperties([
       'entity_type' => 'entity_test_with_bundle',
       'bundle' => 'another',
+      'field_type' => 'string',
+    ]);
+  }
+
+  /**
+   * Tests that the field type plugin's existence is validated.
+   */
+  public function testFieldTypePluginIsValidated(): void {
+    // The `field_type` property is immutable, so we need to clone the entity in
+    // order to cleanly change its field_type property to some invalid value.
+    $this->entity = $this->entity->createDuplicate()
+      ->set('field_type', 'invalid');
+    $this->assertValidationErrors([
+      'field_type' => "The 'invalid' plugin does not exist.",
     ]);
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayBaseTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayBaseTest.php
index 435d2fdd5685281f850ab6d6ab78fcbfe4205c49..1302c0243b21fd8ded1cd2a7b4ebcfd230f240ea 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayBaseTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayBaseTest.php
@@ -22,6 +22,7 @@ class EntityDisplayBaseTest extends KernelTestBase {
     'entity_test',
     'entity_test_third_party',
     'field',
+    'field_test',
     'system',
     'comment',
     'user',
@@ -47,9 +48,9 @@ public function testPreSave() {
       'mode' => 'default',
       'status' => TRUE,
       'content' => [
-        'foo' => ['type' => 'visible'],
+        'foo' => ['type' => 'field_no_settings'],
         'bar' => ['region' => 'hidden'],
-        'name' => ['type' => 'hidden', 'region' => 'content'],
+        'name' => ['type' => 'field_no_settings', 'region' => 'content'],
       ],
     ]);
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
index 8596a96365fe98fe2be7b875e09f6822c373ee18..58c01c69bc25598fbc994055ed03d49a303f2b00 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\KernelTests\Core\Entity;
 
+use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
+use Drupal\layout_builder\Section;
 use Drupal\Core\Entity\Entity\EntityViewMode;
 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
 use Drupal\entity_test\Entity\EntityTestBundle;
@@ -52,6 +54,26 @@ protected function setUp(): void {
     $this->entity->save();
   }
 
+  /**
+   * Tests that the plugin ID of a Layout Builder section is validated.
+   */
+  public function testLayoutSectionPluginIdIsValidated(): void {
+    $this->enableModules(['layout_builder', 'layout_discovery']);
+
+    $this->entity = $this->container->get('entity_display.repository')
+      ->getViewDisplay('user', 'user');
+    $this->assertInstanceOf(LayoutEntityDisplayInterface::class, $this->entity);
+    $this->entity->enableLayoutBuilder()->save();
+    $sections = array_map(fn(Section $section) => $section->toArray(), $this->entity->getSections());
+    $this->assertCount(1, $sections);
+    $sections[0]['layout_id'] = 'non_existent';
+
+    $this->entity->setThirdPartySetting('layout_builder', 'sections', $sections);
+    $this->assertValidationErrors([
+      'third_party_settings.layout_builder.sections.0.layout_id' => "The 'non_existent' plugin does not exist.",
+    ]);
+  }
+
   /**
    * Tests that the target bundle of the entity view display is checked.
    */
diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
index 3106db3269687a7de687447d4a223e0a3010f7f4..e430c21448ee50cfa36b31640c549748dea9a17a 100644
--- a/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\KernelTests\Core\Plugin;
 
+use Drupal\Component\Plugin\FallbackPluginManagerInterface;
+use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Action\ActionInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\KernelTests\KernelTestBase;
@@ -68,4 +70,42 @@ public function testValidation(): void {
     $this->assertCount(0, $violations);
   }
 
+  /**
+   * Tests that fallback plugin IDs can be considered valid or invalid.
+   */
+  public function testFallbackPluginIds(): void {
+    $plugin_manager = $this->prophesize(PluginManagerInterface::class)
+      ->willImplement(FallbackPluginManagerInterface::class);
+    $plugin_manager->getFallbackPluginId('non_existent')
+      ->shouldBeCalledOnce()
+      ->willReturn('broken');
+    $plugin_manager->getDefinition('non_existent', FALSE)
+      ->shouldBeCalled()
+      ->willReturn(NULL);
+    $plugin_manager->getDefinition('broken', FALSE)
+      ->shouldBeCalled()
+      ->willReturn(['id' => 'broken']);
+    $this->container->set('plugin.manager.test_fallback', $plugin_manager->reveal());
+
+    // If fallback plugin IDs are allowed, then an invalid plugin ID should not
+    // raise an error.
+    $definition = DataDefinition::create('string')
+      ->addConstraint('PluginExists', [
+        'manager' => 'plugin.manager.test_fallback',
+        'allowFallback' => TRUE,
+      ]);
+    $data = $this->container->get('typed_data_manager')->create($definition);
+    $data->setValue('non_existent');
+    $this->assertCount(0, $data->validate());
+
+    // If fallback plugin IDs are not considered valid (the default behavior),
+    // then we should get a validation error.
+    $definition->addConstraint('PluginExists', [
+      'manager' => 'plugin.manager.test_fallback',
+    ]);
+    $violations = $data->validate();
+    $this->assertCount(1, $violations);
+    $this->assertSame("The 'non_existent' plugin does not exist.", (string) $violations->get(0)->getMessage());
+  }
+
 }