diff --git a/fdbcli/ThrottleCommand.actor.cpp b/fdbcli/ThrottleCommand.actor.cpp
index 3909656eb9..5f8bd4e68f 100644
--- a/fdbcli/ThrottleCommand.actor.cpp
+++ b/fdbcli/ThrottleCommand.actor.cpp
@@ -185,7 +185,7 @@ ACTOR Future<bool> throttleCommandActor(Reference<IDatabase> db, std::vector<Str
 		printf("Tag `%s' has been throttled\n", tokens[3].toString().c_str());
 	} else if (tokencmp(tokens[1], "off")) {
 		int nextIndex = 2;
-		TagSet tagSet;
+		state TagSet tagSet;
 		bool throttleTypeSpecified = false;
 		bool is_error = false;
 		Optional<TagThrottleType> throttleType = TagThrottleType::MANUAL;
@@ -260,12 +260,12 @@ ACTOR Future<bool> throttleCommandActor(Reference<IDatabase> db, std::vector<Str
 			if (tagSet.size() > 0) {
 				bool success = wait(ThrottleApi::unthrottleTags(db, tagSet, throttleType, priority));
 				if (success) {
-					printf("Unthrottled tag `%s'%s\n", tokens[3].toString().c_str(), priorityString.c_str());
+					fmt::print("Unthrottled {0}{1}\n", tagSet.toString(), priorityString);
 				} else {
-					printf("Tag `%s' was not %sthrottled%s\n",
-					       tokens[3].toString().c_str(),
-					       throttleTypeString,
-					       priorityString.c_str());
+					fmt::print("{0} was not {1}throttled{2}\n",
+					           tagSet.toString(Capitalize::True),
+					           throttleTypeString,
+					           priorityString);
 				}
 			} else {
 				bool unthrottled = wait(ThrottleApi::unthrottleAll(db, throttleType, priority));
diff --git a/fdbclient/TagThrottle.actor.cpp b/fdbclient/TagThrottle.actor.cpp
index fd1d845db2..613cfa3d90 100644
--- a/fdbclient/TagThrottle.actor.cpp
+++ b/fdbclient/TagThrottle.actor.cpp
@@ -47,6 +47,19 @@ size_t TagSet::size() const {
 	return tags.size();
 }
 
+std::string TagSet::toString(Capitalize capitalize) const {
+	ASSERT(!tags.empty());
+	if (tags.size() == 1) {
+		std::string start = capitalize ? "Tag" : "tag";
+		return format("%s `%s'", start.c_str(), tags[0].toString().c_str());
+	}
+	std::string result = capitalize ? "Tags (" : "tags (";
+	for (int index = 0; index < tags.size() - 1; ++index) {
+		result += format("`%s', ", tags[index].toString().c_str());
+	}
+	return result + format("`%s')", tags.back().toString().c_str());
+}
+
 // Format for throttle key:
 //
 // tagThrottleKeysPrefix + [auto-throttled (1-byte 0/1)] + [priority (1-byte)] + [tag list]
@@ -112,3 +125,24 @@ TagThrottleValue TagThrottleValue::fromValue(const ValueRef& value) {
 }
 
 FDB_DEFINE_BOOLEAN_PARAM(ContainsRecommended);
+FDB_DEFINE_BOOLEAN_PARAM(Capitalize);
+
+TEST_CASE("TagSet/toString") {
+	{
+		TagSet tagSet;
+		tagSet.addTag("a"_sr);
+		ASSERT(tagSet.toString() == "tag `a'");
+		ASSERT(tagSet.toString(Capitalize::True) == "Tag `a'");
+	}
+	{
+		// Order is not guaranteed when multiple tags are present
+		TagSet tagSet;
+		tagSet.addTag("a"_sr);
+		tagSet.addTag("b"_sr);
+		auto tagString = tagSet.toString();
+		ASSERT(tagString == "tags (`a', `b')" || tagString == "tags (`b', `a')");
+		auto capitalizedTagString = tagSet.toString(Capitalize::True);
+		ASSERT(capitalizedTagString == "Tags (`a', `b')" || capitalizedTagString == "Tags (`b', `a')");
+	}
+	return Void();
+}
diff --git a/fdbclient/TagThrottle.actor.h b/fdbclient/TagThrottle.actor.h
index 44e09da12f..d8d4f50d0c 100644
--- a/fdbclient/TagThrottle.actor.h
+++ b/fdbclient/TagThrottle.actor.h
@@ -41,6 +41,7 @@ typedef StringRef TransactionTagRef;
 typedef Standalone<TransactionTagRef> TransactionTag;
 
 FDB_DECLARE_BOOLEAN_PARAM(ContainsRecommended);
+FDB_DECLARE_BOOLEAN_PARAM(Capitalize);
 
 class TagSet {
 public:
@@ -97,6 +98,9 @@ public:
 
 	const Arena& getArena() const { return arena; }
 
+	// Used by fdbcli commands
+	std::string toString(Capitalize = Capitalize::False) const;
+
 private:
 	size_t bytes;
 	Arena arena;