diff --git a/src/mongo/db/s/collmod_coordinator.cpp b/src/mongo/db/s/collmod_coordinator.cpp
index f3dba24ecb0..ee5f4296e8a 100644
--- a/src/mongo/db/s/collmod_coordinator.cpp
+++ b/src/mongo/db/s/collmod_coordinator.cpp
@@ -78,6 +78,16 @@ bool hasTimeSeriesBucketingUpdate(const CollModRequest& request) {
     return ts->getGranularity() || ts->getBucketMaxSpanSeconds() || ts->getBucketRoundingSeconds();
 }
 
+bool isRetriableErrorForDDLCoordinator(const Status& status) {
+    return status.isA<ErrorCategory::CursorInvalidatedError>() ||
+        status.isA<ErrorCategory::ShutdownError>() || status.isA<ErrorCategory::RetriableError>() ||
+        status.isA<ErrorCategory::CancellationError>() ||
+        status.isA<ErrorCategory::ExceededTimeLimitError>() ||
+        status.isA<ErrorCategory::WriteConcernError>() ||
+        status == ErrorCodes::FailedToSatisfyReadPreference || status == ErrorCodes::Interrupted ||
+        status == ErrorCodes::LockBusy || status == ErrorCodes::CommandNotFound;
+}
+
 }  // namespace
 
 CollModCoordinator::CollModCoordinator(ShardingDDLCoordinatorService* service,
@@ -285,74 +295,98 @@ ExecutorFuture<void> CollModCoordinator::_runImpl(
                 _saveShardingInfoOnCoordinatorIfNecessary(opCtx);
 
                 if (_collInfo->isSharded) {
-                    ShardsvrCollModParticipant request(originalNss(), _request);
-                    bool needsUnblock =
-                        _collInfo->timeSeriesOptions && hasTimeSeriesBucketingUpdate(_request);
-                    request.setNeedsUnblock(needsUnblock);
-
-                    std::vector<AsyncRequestsSender::Response> responses;
-                    auto shardsOwningChunks = _shardingInfo->shardsOwningChunks;
-                    auto primaryShardOwningChunk = std::find(shardsOwningChunks.begin(),
-                                                             shardsOwningChunks.end(),
-                                                             _shardingInfo->primaryShard);
-
-                    // If trying to convert an index to unique, executes a dryRun first to find any
-                    // duplicates without actually changing the indexes to avoid inconsistent index
-                    // specs on different shards.
-                    // Example:
-                    //   Shard0: {_id: 0, a: 1}
-                    //   Shard1: {_id: 1, a: 2}, {_id: 2, a: 2}
-                    //   When trying to convert index {a: 1} to unique, the dry run will return the
-                    //   duplicate errors to the user without converting the indexes.
-                    if (isCollModIndexUniqueConversion(_request)) {
-                        // The 'dryRun' option only works with 'unique' index option. We need to
-                        // strip out other incompatible options.
-                        auto dryRunRequest = ShardsvrCollModParticipant{
-                            originalNss(), makeCollModDryRunRequest(_request)};
-                        sharding_ddl_util::sendAuthenticatedCommandToShards(
-                            opCtx,
-                            nss().db(),
-                            CommandHelpers::appendMajorityWriteConcern(dryRunRequest.toBSON({})),
-                            shardsOwningChunks,
-                            **executor);
-                    }
-
-                    // A view definition will only be present on the primary shard. So we pass an
-                    // addition 'performViewChange' flag only to the primary shard.
-                    if (primaryShardOwningChunk != shardsOwningChunks.end()) {
-                        request.setPerformViewChange(true);
-                        const auto& primaryResponse =
+                    try {
+                        if (!_firstExecution) {
+                            if (_result.is_initialized() &&
+                                sharding_ddl_util::checkAllowMigrations(
+                                    opCtx, _collInfo->nsForTargeting)) {
+                                // The command finished and we have the response. Return it.
+                                return;
+                            } else {
+                                // We don't have a result, so either the command didn't finish or it
+                                // finished on a different node and we lost the result. Stop
+                                // migrations in case it is the latter, and retry the command.
+                                sharding_ddl_util::stopMigrations(
+                                    opCtx, _collInfo->nsForTargeting, _doc.getCollUUID());
+                            }
+                        }
+
+                        ShardsvrCollModParticipant request(originalNss(), _request);
+                        bool needsUnblock =
+                            _collInfo->timeSeriesOptions && hasTimeSeriesBucketingUpdate(_request);
+                        request.setNeedsUnblock(needsUnblock);
+
+                        std::vector<AsyncRequestsSender::Response> responses;
+                        auto shardsOwningChunks = _shardingInfo->shardsOwningChunks;
+                        auto primaryShardOwningChunk = std::find(shardsOwningChunks.begin(),
+                                                                 shardsOwningChunks.end(),
+                                                                 _shardingInfo->primaryShard);
+
+                        // If trying to convert an index to unique, executes a dryRun first to find
+                        // any duplicates without actually changing the indexes to avoid
+                        // inconsistent index specs on different shards. Example:
+                        //   Shard0: {_id: 0, a: 1}
+                        //   Shard1: {_id: 1, a: 2}, {_id: 2, a: 2}
+                        //   When trying to convert index {a: 1} to unique, the dry run will return
+                        //   the duplicate errors to the user without converting the indexes.
+                        if (isCollModIndexUniqueConversion(_request)) {
+                            // The 'dryRun' option only works with 'unique' index option. We need to
+                            // strip out other incompatible options.
+                            auto dryRunRequest = ShardsvrCollModParticipant{
+                                originalNss(), makeCollModDryRunRequest(_request)};
+                            sharding_ddl_util::sendAuthenticatedCommandToShards(
+                                opCtx,
+                                nss().db(),
+                                CommandHelpers::appendMajorityWriteConcern(
+                                    dryRunRequest.toBSON({})),
+                                shardsOwningChunks,
+                                **executor);
+                        }
+
+                        // A view definition will only be present on the primary shard. So we pass
+                        // an addition 'performViewChange' flag only to the primary shard.
+                        if (primaryShardOwningChunk != shardsOwningChunks.end()) {
+                            request.setPerformViewChange(true);
+                            const auto& primaryResponse =
+                                sharding_ddl_util::sendAuthenticatedCommandToShards(
+                                    opCtx,
+                                    nss().db(),
+                                    CommandHelpers::appendMajorityWriteConcern(request.toBSON({})),
+                                    {_shardingInfo->primaryShard},
+                                    **executor);
+                            responses.insert(
+                                responses.end(), primaryResponse.begin(), primaryResponse.end());
+                            shardsOwningChunks.erase(primaryShardOwningChunk);
+                        }
+
+                        request.setPerformViewChange(false);
+                        const auto& secondaryResponses =
                             sharding_ddl_util::sendAuthenticatedCommandToShards(
                                 opCtx,
                                 nss().db(),
                                 CommandHelpers::appendMajorityWriteConcern(request.toBSON({})),
-                                {_shardingInfo->primaryShard},
+                                shardsOwningChunks,
                                 **executor);
                         responses.insert(
-                            responses.end(), primaryResponse.begin(), primaryResponse.end());
-                        shardsOwningChunks.erase(primaryShardOwningChunk);
-                    }
-
-                    request.setPerformViewChange(false);
-                    const auto& secondaryResponses =
-                        sharding_ddl_util::sendAuthenticatedCommandToShards(
-                            opCtx,
-                            nss().db(),
-                            CommandHelpers::appendMajorityWriteConcern(request.toBSON({})),
-                            shardsOwningChunks,
-                            **executor);
-                    responses.insert(
-                        responses.end(), secondaryResponses.begin(), secondaryResponses.end());
-
-                    BSONObjBuilder builder;
-                    std::string errmsg;
-                    auto ok = appendRawResponses(opCtx, &errmsg, &builder, responses).responseOK;
-                    if (!errmsg.empty()) {
-                        CommandHelpers::appendSimpleCommandStatus(builder, ok, errmsg);
+                            responses.end(), secondaryResponses.begin(), secondaryResponses.end());
+
+                        BSONObjBuilder builder;
+                        std::string errmsg;
+                        auto ok =
+                            appendRawResponses(opCtx, &errmsg, &builder, responses).responseOK;
+                        if (!errmsg.empty()) {
+                            CommandHelpers::appendSimpleCommandStatus(builder, ok, errmsg);
+                        }
+                        _result = builder.obj();
+                        sharding_ddl_util::resumeMigrations(
+                            opCtx, _collInfo->nsForTargeting, _doc.getCollUUID());
+                    } catch (DBException& ex) {
+                        if (!_isRetriableErrorForDDLCoordinator(ex.toStatus())) {
+                            sharding_ddl_util::resumeMigrations(
+                                opCtx, _collInfo->nsForTargeting, _doc.getCollUUID());
+                        }
+                        throw;
                     }
-                    _result = builder.obj();
-                    sharding_ddl_util::resumeMigrations(
-                        opCtx, _collInfo->nsForTargeting, _doc.getCollUUID());
                 } else {
                     CollMod cmd(originalNss());
                     cmd.setCollModRequest(_request);
@@ -380,16 +414,6 @@ ExecutorFuture<void> CollModCoordinator::_runImpl(
                             "Error running collMod",
                             "namespace"_attr = nss(),
                             "error"_attr = redact(status));
-                // If we have the collection UUID set, this error happened in a sharded collection,
-                // we should restore the migrations.
-                if (_doc.getCollUUID() && _collInfo) {
-                    auto opCtxHolder = cc().makeOperationContext();
-                    auto* opCtx = opCtxHolder.get();
-                    getForwardableOpMetadata().setOn(opCtx);
-
-                    sharding_ddl_util::resumeMigrations(
-                        opCtx, _collInfo->nsForTargeting, _doc.getCollUUID());
-                }
             }
             return status;
         });
diff --git a/src/mongo/db/s/sharding_ddl_coordinator.cpp b/src/mongo/db/s/sharding_ddl_coordinator.cpp
index d695d714050..2a418d86e44 100644
--- a/src/mongo/db/s/sharding_ddl_coordinator.cpp
+++ b/src/mongo/db/s/sharding_ddl_coordinator.cpp
@@ -61,16 +61,6 @@ namespace {
 
 const Backoff kExponentialBackoff(Seconds(1), Milliseconds::max());
 
-bool isRetriableErrorForDDLCoordinator(const Status& status) {
-    return status.isA<ErrorCategory::CursorInvalidatedError>() ||
-        status.isA<ErrorCategory::ShutdownError>() || status.isA<ErrorCategory::RetriableError>() ||
-        status.isA<ErrorCategory::CancellationError>() ||
-        status.isA<ErrorCategory::ExceededTimeLimitError>() ||
-        status.isA<ErrorCategory::WriteConcernError>() ||
-        status == ErrorCodes::FailedToSatisfyReadPreference || status == ErrorCodes::Interrupted ||
-        status == ErrorCodes::LockBusy || status == ErrorCodes::CommandNotFound;
-}
-
 }  // namespace
 
 ShardingDDLCoordinatorMetadata extractShardingDDLCoordinatorMetadata(const BSONObj& coorDoc) {
@@ -369,7 +359,7 @@ SemiFuture<void> ShardingDDLCoordinator::run(std::shared_ptr<executor::ScopedTas
                     //  If the token is not cancelled we retry because it could have been generated
                     //  by a remote node.
                     if (!status.isOK() && !_completeOnError &&
-                        (_mustAlwaysMakeProgress() || isRetriableErrorForDDLCoordinator(status)) &&
+                        (_mustAlwaysMakeProgress() || _isRetriableErrorForDDLCoordinator(status)) &&
                         !token.isCanceled()) {
                         LOGV2_INFO(5656000,
                                    "Re-executing sharding DDL coordinator",
@@ -471,4 +461,14 @@ void ShardingDDLCoordinator::_performNoopRetryableWriteOnAllShardsAndConfigsvr(
     sharding_ddl_util::performNoopRetryableWriteOnShards(opCtx, shardsAndConfigsvr, osi, executor);
 }
 
+bool ShardingDDLCoordinator::_isRetriableErrorForDDLCoordinator(const Status& status) {
+    return status.isA<ErrorCategory::CursorInvalidatedError>() ||
+        status.isA<ErrorCategory::ShutdownError>() || status.isA<ErrorCategory::RetriableError>() ||
+        status.isA<ErrorCategory::CancellationError>() ||
+        status.isA<ErrorCategory::ExceededTimeLimitError>() ||
+        status.isA<ErrorCategory::WriteConcernError>() ||
+        status == ErrorCodes::FailedToSatisfyReadPreference || status == ErrorCodes::Interrupted ||
+        status == ErrorCodes::LockBusy || status == ErrorCodes::CommandNotFound;
+}
+
 }  // namespace mongo
diff --git a/src/mongo/db/s/sharding_ddl_coordinator.h b/src/mongo/db/s/sharding_ddl_coordinator.h
index 0e7462f10bc..d00dca97db8 100644
--- a/src/mongo/db/s/sharding_ddl_coordinator.h
+++ b/src/mongo/db/s/sharding_ddl_coordinator.h
@@ -137,6 +137,11 @@ protected:
         return false;
     };
 
+    /*
+     * Specify if the given error will be retried by the ddl coordinator infrastructure.
+     */
+    bool _isRetriableErrorForDDLCoordinator(const Status& status);
+
     ShardingDDLCoordinatorService* _service;
     const ShardingDDLCoordinatorId _coordId;
 
diff --git a/src/mongo/db/s/sharding_ddl_util.cpp b/src/mongo/db/s/sharding_ddl_util.cpp
index b180eb10a05..1b85cb2820f 100644
--- a/src/mongo/db/s/sharding_ddl_util.cpp
+++ b/src/mongo/db/s/sharding_ddl_util.cpp
@@ -733,6 +733,26 @@ void resumeMigrations(OperationContext* opCtx,
     setAllowMigrations(opCtx, nss, expectedCollectionUUID, true);
 }
 
+bool checkAllowMigrations(OperationContext* opCtx, const NamespaceString& nss) {
+    auto collDoc =
+        uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getConfigShard()->exhaustiveFindOnConfig(
+                            opCtx,
+                            ReadPreferenceSetting(ReadPreference::PrimaryOnly, TagSet{}),
+                            repl::ReadConcernLevel::kMajorityReadConcern,
+                            CollectionType::ConfigNS,
+                            BSON(CollectionType::kNssFieldName << nss.ns()),
+                            BSONObj(),
+                            1))
+            .docs;
+
+    uassert(ErrorCodes::NamespaceNotFound,
+            str::stream() << "collection " << nss.ns() << " not found",
+            !collDoc.empty());
+
+    auto coll = CollectionType(collDoc[0]);
+    return coll.getAllowMigrations();
+}
+
 boost::optional<UUID> getCollectionUUID(OperationContext* opCtx,
                                         const NamespaceString& nss,
                                         bool allowViews) {
diff --git a/src/mongo/db/s/sharding_ddl_util.h b/src/mongo/db/s/sharding_ddl_util.h
index 6b907f0e08f..4f49cce2de7 100644
--- a/src/mongo/db/s/sharding_ddl_util.h
+++ b/src/mongo/db/s/sharding_ddl_util.h
@@ -173,6 +173,12 @@ void resumeMigrations(OperationContext* opCtx,
                       const NamespaceString& nss,
                       const boost::optional<UUID>& expectedCollectionUUID);
 
+/**
+ * Calls to the config server primary to get the collection document for the given nss.
+ * Returns the value of the allowMigrations flag on the collection document.
+ */
+bool checkAllowMigrations(OperationContext* opCtx, const NamespaceString& nss);
+
 /*
  * Returns the UUID of the collection (if exists) using the catalog. It does not provide any locking
  * guarantees after the call.
