diff --git a/jstests/sharding/repro-tassert-7531700.js b/jstests/sharding/repro-tassert-7531700.js
new file mode 100644
index 00000000000..41011d7b500
--- /dev/null
+++ b/jstests/sharding/repro-tassert-7531700.js
@@ -0,0 +1,77 @@
+import {configureFailPoint} from "jstests/libs/fail_point_util.js";
+import {ShardingTest} from "jstests/libs/shardingtest.js";
+import {CreateShardedCollectionUtil} from "jstests/sharding/libs/create_sharded_collection_util.js";
+import {moveOutSessionChunks} from "jstests/sharding/libs/remove_shard_util.js";
+
+var st = new ShardingTest({shards: 2, config: 1, configShard: true});
+
+moveOutSessionChunks(st, "config", st.shard1.shardName);
+
+// Create a collection with a chunk on the config shard, and then move it out.
+// The local collection will still exist on the config shard, but without owning any chunks.
+const coll = st.getDB("test")["foo"];
+CreateShardedCollectionUtil.shardCollectionWithChunks(coll, {x: 1}, [
+    {min: {x: MinKey}, max: {x: 0}, shard: "config"},
+    {min: {x: 0}, max: {x: MaxKey}, shard: st.shard1.shardName},
+]);
+
+assert.commandWorked(st.s.adminCommand({moveChunk: coll.getFullName(), find: {x: -1}, to: st.shard1.shardName}));
+
+// When validating the shard key index consistency, checkMetadataConsistency first checks it
+// optimistically, without taking the collection lock or checking if the shard owns chunks.
+// We need this check to fail to get to the point we want. To do this, we refine the shard key.
+// Because the config shard no longer owns any chunks, it will not get that index created locally,
+// so it will make the optimistic check fail.
+assert.commandWorked(coll.createIndex({x: 1, y: 1}));
+assert.commandWorked(st.s.adminCommand({refineCollectionShardKey: "test.foo", key: { x: 1, y: 1 }}));
+
+// Start a transitionToDedicatedConfigsvr procedure. Make it hang at a certain point of its
+// execution.
+let fpHangRemoveShardAfterDrainingMigrationOnConfigsvr = configureFailPoint(
+    st.configRS.getPrimary(), 'hangRemoveShardAfterDrainingMigrationsOnConfigsvr');
+
+const awaitTransitionToDedicated = startParallelShell(async () => {
+    const {removeShard} = await import("jstests/sharding/libs/remove_shard_util.js");
+    removeShard(db, "config");
+}, st.s.port);
+
+jsTest.log("Waiting to hit fpHangRemoveShardAfterDrainingMigrationOnConfigsvr");
+fpHangRemoveShardAfterDrainingMigrationOnConfigsvr.wait();
+jsTest.log("Hit fpHangRemoveShardAfterDrainingMigrationOnConfigsvr");
+
+// Run checkMetadataConsistency. It will see the collection on both the local and sharding catalog,
+// and then it will check the shard key index consistency. The optimistic check will fail.
+// We then stop it there before it takes the collection lock.
+let fpHangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails = configureFailPoint(
+    st.configRS.getPrimary(), 'hangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails');
+
+const awaitCheckMetadataConsistency = startParallelShell(() => {
+    const metadataConsistencyCheckResult =
+        db.getSiblingDB("test").checkMetadataConsistency().toArray();
+    jsTest.log("checkMetadataConsistency done: " + tojson(metadataConsistencyCheckResult));
+    assert.eq(0,
+              metadataConsistencyCheckResult.length,
+              "Detected metadata inconsistencies: " + tojson(metadataConsistencyCheckResult));
+}, st.s.port);
+fpHangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails.wait();
+
+// Now let the transition continue so that it drops the local collection on the config shard.
+const fpHangRemoveShardAfterDrainComplete =
+    configureFailPoint(st.configRS.getPrimary(), "hangRemoveShardAfterDrainComplete");
+fpHangRemoveShardAfterDrainingMigrationOnConfigsvr.off();
+
+jsTest.log("Waiting to hit fpHangRemoveShardAfterDrainComplete");
+fpHangRemoveShardAfterDrainComplete.wait();
+jsTest.log("Hit fpHangRemoveShardAfterDrainComplete");
+
+// Release the checkMetadataConsistency operation. Now, after is saw both the collection on both
+// local and sharding catalog, the local collection will have been dropped by the shard transition.
+// This will trip a tassert inside checkMetadataConsistency.
+fpHangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails.off();
+awaitCheckMetadataConsistency();
+
+fpHangRemoveShardAfterDrainComplete.off();
+awaitTransitionToDedicated();
+
+
+st.stop();
diff --git a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp
index 7e2e861030f..1934a0431e9 100644
--- a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp
+++ b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp
@@ -181,6 +181,10 @@ MONGO_FAIL_POINT_DEFINE(skipUpdatingClusterCardinalityParameterAfterAddShard);
 MONGO_FAIL_POINT_DEFINE(skipUpdatingClusterCardinalityParameterAfterRemoveShard);
 MONGO_FAIL_POINT_DEFINE(skipBlockingDDLCoordinatorsDuringAddAndRemoveShard);
 
+MONGO_FAIL_POINT_DEFINE(hangRemoveShardAfterDrainingMigrationsOnConfigsvr);
+MONGO_FAIL_POINT_DEFINE(hangRemoveShardAfterDrainComplete);
+
+
 using CallbackHandle = executor::TaskExecutor::CallbackHandle;
 using CallbackArgs = executor::TaskExecutor::CallbackArgs;
 using RemoteCommandCallbackArgs = executor::TaskExecutor::RemoteCommandCallbackArgs;
@@ -1519,6 +1523,8 @@ RemoveShardProgress ShardingCatalogManager::removeShard(OperationContext* opCtx,
         }
     }
 
+    hangRemoveShardAfterDrainingMigrationsOnConfigsvr.pauseWhileSet();
+
     // Prevent new ShardingDDLCoordinators operations from starting across the cluster.
     ScopeGuard unblockDDLCoordinatorsGuard([&] { scheduleAsyncUnblockDDLCoordinators(opCtx); });
     if (feature_flags::gStopDDLCoordinatorsDuringTopologyChanges.isEnabled(
@@ -1601,6 +1607,7 @@ RemoveShardProgress ShardingCatalogManager::removeShard(OperationContext* opCtx,
 
     // Draining is done, now finish removing the shard.
     LOGV2(21949, "Going to remove shard", "shardId"_attr = name);
+    hangRemoveShardAfterDrainComplete.pauseWhileSet();
 
     // Synchronize the control shard selection, the shard's document removal, and the topology time
     // update to exclude potential race conditions in case of concurrent add/remove shard
diff --git a/src/mongo/db/s/metadata_consistency_util.cpp b/src/mongo/db/s/metadata_consistency_util.cpp
index e0a0a1deedf..465ccf15175 100644
--- a/src/mongo/db/s/metadata_consistency_util.cpp
+++ b/src/mongo/db/s/metadata_consistency_util.cpp
@@ -86,6 +86,7 @@ namespace metadata_consistency_util {
 namespace {
 
 MONGO_FAIL_POINT_DEFINE(insertFakeInconsistencies);
+MONGO_FAIL_POINT_DEFINE(hangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails);
 
 /*
  * Emit a warning log containing information about the given inconsistency
@@ -133,6 +134,8 @@ void _checkShardKeyIndexInconsistencies(OperationContext* opCtx,
         return;
     }
 
+    hangCheckMetadataConsistencyAfterOptimisticShardKeyCheckFails.pauseWhileSet();
+
     // Pessimistic check under collection lock to serialize with chunk migration commit.
     AutoGetCollection ac(opCtx, nss, MODE_IS);
     tassert(7531700,
