* NB: Changing the requirements here also requires adapting
     * CheckSlotRequirements() and CheckLogicalDecodingRequirements().
     */
-   if (cp.slotdata.database != InvalidOid && wal_level < WAL_LEVEL_LOGICAL)
-       ereport(FATAL,
-               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                errmsg("logical replication slot \"%s\" exists, but wal_level < logical",
-                       NameStr(cp.slotdata.name)),
-                errhint("Change wal_level to be logical or higher.")));
+   if (cp.slotdata.database != InvalidOid)
+   {
+       if (wal_level < WAL_LEVEL_LOGICAL)
+           ereport(FATAL,
+                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                    errmsg("logical replication slot \"%s\" exists, but wal_level < logical",
+                           NameStr(cp.slotdata.name)),
+                    errhint("Change wal_level to be logical or higher.")));
+
+       /*
+        * In standby mode, the hot standby must be enabled. This check is
+        * necessary to ensure logical slots are invalidated when they become
+        * incompatible due to insufficient wal_level. Otherwise, if the
+        * primary reduces wal_level < logical while hot standby is disabled,
+        * logical slots would remain valid even after promotion.
+        */
+       if (StandbyMode && !EnableHotStandby)
+           ereport(FATAL,
+                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                    errmsg("logical replication slot \"%s\" exists on the standby, but hot_standby = off",
+                           NameStr(cp.slotdata.name)),
+                    errhint("Change hot_standby to be on.")));
+   }
    else if (wal_level < WAL_LEVEL_REPLICA)
        ereport(FATAL,
                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 
    \$psql_subscriber{subscriber_stderr},
    IPC::Run::timeout($default_timeout));
 
+##################################################
+# Test that the standby requires hot_standby to be
+# enabled for pre-existing logical slots.
+##################################################
+
+# create the logical slots
+$node_standby->create_logical_slot_on_standby($node_primary, 'restart_test');
+$node_standby->stop;
+$node_standby->append_conf('postgresql.conf', qq[hot_standby = off]);
+
+# Use run_log instead of $node_standby->start because this test expects
+# that the server ends with an error during startup.
+run_log(
+   [
+       'pg_ctl',
+       '--pgdata' => $node_standby->data_dir,
+       '--log' => $node_standby->logfile,
+       'start',
+   ]);
+
+# wait for postgres to terminate
+foreach my $i (0 .. 10 * $PostgreSQL::Test::Utils::timeout_default)
+{
+   last if !-f $node_standby->data_dir . '/postmaster.pid';
+   usleep(100_000);
+}
+
+# Confirm that the server startup fails with an expected error
+my $logfile = slurp_file($node_standby->logfile());
+ok( $logfile =~
+     qr/FATAL: .* logical replication slot ".*" exists on the standby, but hot_standby = off/,
+   "the standby ends with an error during startup because hot_standby was disabled"
+);
+$node_standby->adjust_conf('postgresql.conf', 'hot_standby', 'on');
+$node_standby->start;
+$node_standby->safe_psql('postgres',
+   qq[SELECT pg_drop_replication_slot('restart_test')]);
+
 ##################################################
 # Test that logical decoding on the standby
 # behaves correctly.