diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 79b54c1baf295..f98ee2961f530 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -8829,6 +8829,9 @@ To shut down the device, press and hold the power button on the device again.
       <message name="IDS_ASH_BIRCH_CORAL_ADDON_SELECTOR_HIDDEN" translateable="false" desc="The accessible name for the birch coral button selector UI.">
         Show
       </message>
+      <message name="IDS_ASH_BIRCH_CORAL_SAVED_GROUPS_MAX_NUM_REACHED" translateable="false" desc="Message shown to users when they attempt to save a new coral group when the maximum number of coral saved groups has been reached.">
+        Maximum number of saved groups reached.
+      </message>
 
       <!-- Graduation app strings -->
       <message name="IDS_ASH_GRADUATION_NUDGE_TEXT" desc="Text shown in nudge that is displayed after the Content Transfer app becomes available.">
diff --git a/ash/constants/notifier_catalogs.h b/ash/constants/notifier_catalogs.h
index 0a0f6011b203f..2ffdccf0e3a6a 100644
--- a/ash/constants/notifier_catalogs.h
+++ b/ash/constants/notifier_catalogs.h
@@ -325,7 +325,8 @@ enum class ToastCatalogName {
   kOnTaskUrlBlocked = 55,
   kCopyImageToClipboardAction = 56,
   kCaptureModeTextCopied = 57,
-  kMaxValue = kCaptureModeTextCopied
+  kCoralSavedGroupLimitMax = 58,
+  kMaxValue = kCoralSavedGroupLimitMax
 };
 
 }  // namespace ash
diff --git a/ash/wm/coral/coral_controller_unittest.cc b/ash/wm/coral/coral_controller_unittest.cc
index 46fe781c96ee2..a639e0931612f 100644
--- a/ash/wm/coral/coral_controller_unittest.cc
+++ b/ash/wm/coral/coral_controller_unittest.cc
@@ -13,23 +13,36 @@
 #include "ash/shell.h"
 #include "ash/system/toast/toast_manager_impl.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/ash_test_helper.h"
 #include "ash/wm/coral/coral_test_util.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_test_util.h"
+#include "ash/wm/desks/templates/saved_desk_test_helper.h"
+#include "ash/wm/desks/templates/saved_desk_test_util.h"
+#include "ash/wm/overview/birch/birch_bar_controller.h"
+#include "ash/wm/overview/birch/birch_bar_menu_model_adapter.h"
 #include "ash/wm/overview/birch/birch_chip_button.h"
+#include "ash/wm/overview/birch/birch_chip_context_menu_model.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/snap_group/snap_group_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/view_utils.h"
 
 namespace ash {
 
 class CoralControllerTest : public AshTestBase {
  public:
+  CoralControllerTest() {
+    feature_list_.InitWithFeatures(
+        {features::kCoralFeature, features::kCoralSavedDeskFeature}, {});
+  }
+
   void ClickFirstCoralButton() {
     DeskSwitchAnimationWaiter waiter;
     BirchChipButton* coral_button = GetFirstCoralButton();
@@ -38,6 +51,12 @@ class CoralControllerTest : public AshTestBase {
     waiter.Wait();
   }
 
+  void AddCoralEntry(const std::string& name) {
+    AddSavedDeskEntry(ash_test_helper()->saved_desk_test_helper()->desk_model(),
+                      base::Uuid::GenerateRandomV4(), name, base::Time::Now(),
+                      DeskTemplateType::kCoral);
+  }
+
   void SetUp() override {
     AshTestBase::SetUp();
 
@@ -66,7 +85,7 @@ class CoralControllerTest : public AshTestBase {
  private:
   std::unique_ptr<TestBirchClient> birch_client_;
 
-  base::test::ScopedFeatureList feature_list_{features::kCoralFeature};
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Tests that clicking the in session coral button opens and activates a new
@@ -199,4 +218,34 @@ TEST_F(CoralControllerTest, SnapGroupTwoWindowsInCoralGroup) {
                                                                 window2.get()));
 }
 
+TEST_F(CoralControllerTest, MaxCoralSavedGroupLimit) {
+  ash_test_helper()->saved_desk_test_helper()->WaitForDeskModels();
+
+  // Add enough entries to hit the max.
+  for (int i = 0; i < 6; ++i) {
+    AddCoralEntry(base::NumberToString(i));
+  }
+
+  Shell::Get()->overview_controller()->StartOverview(
+      OverviewStartAction::kTests);
+
+  // Right click on the coral button to bring up the context menu and the save
+  // as group option.
+  RightClickOn(GetFirstCoralButton());
+  BirchBarMenuModelAdapter* model_adapter =
+      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
+  EXPECT_TRUE(model_adapter->IsShowingMenu());
+  views::MenuItemView* save_as_group_item =
+      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(1);
+  ASSERT_EQ(save_as_group_item->GetCommand(),
+            base::to_underlying(
+                BirchChipContextMenuModel::CommandId::kCoralSaveForLater));
+
+  // Left click on the menu item and test that the toast shows up since we
+  // cannot create more coral saved groups.
+  LeftClickOn(save_as_group_item);
+  EXPECT_TRUE(Shell::Get()->toast_manager()->IsToastShown(
+      "coral_max_saved_groups_toast"));
+}
+
 }  // namespace ash
diff --git a/ash/wm/desks/templates/saved_desk_presenter.cc b/ash/wm/desks/templates/saved_desk_presenter.cc
index b9c8099dadbb4..a9c4fec860c2a 100644
--- a/ash/wm/desks/templates/saved_desk_presenter.cc
+++ b/ash/wm/desks/templates/saved_desk_presenter.cc
@@ -330,16 +330,30 @@ SavedDeskPresenter::~SavedDeskPresenter() = default;
 
 size_t SavedDeskPresenter::GetEntryCount(DeskTemplateType type) const {
   auto* model = GetDeskModel();
-  return type == DeskTemplateType::kTemplate
-             ? model->GetDeskTemplateEntryCount()
-             : model->GetSaveAndRecallDeskEntryCount();
+  switch (type) {
+    case DeskTemplateType::kTemplate:
+      return model->GetDeskTemplateEntryCount();
+    case DeskTemplateType::kSaveAndRecall:
+      return model->GetSaveAndRecallDeskEntryCount();
+    case DeskTemplateType::kCoral:
+      return model->GetCoralEntryCount();
+    default:
+      NOTREACHED();
+  }
 }
 
 size_t SavedDeskPresenter::GetMaxEntryCount(DeskTemplateType type) const {
   auto* model = GetDeskModel();
-  return type == DeskTemplateType::kTemplate
-             ? model->GetMaxDeskTemplateEntryCount()
-             : model->GetMaxSaveAndRecallDeskEntryCount();
+  switch (type) {
+    case DeskTemplateType::kTemplate:
+      return model->GetMaxDeskTemplateEntryCount();
+    case DeskTemplateType::kSaveAndRecall:
+      return model->GetMaxSaveAndRecallDeskEntryCount();
+    case DeskTemplateType::kCoral:
+      return model->GetMaxCoralEntryCount();
+    default:
+      NOTREACHED();
+  }
 }
 
 ash::DeskTemplate* SavedDeskPresenter::FindOtherEntryWithName(
diff --git a/ash/wm/overview/birch/birch_chip_button.cc b/ash/wm/overview/birch/birch_chip_button.cc
index 9045a0f33a1e9..ad5b2530a8444 100644
--- a/ash/wm/overview/birch/birch_chip_button.cc
+++ b/ash/wm/overview/birch/birch_chip_button.cc
@@ -6,12 +6,15 @@
 
 #include "ash/birch/birch_coral_provider.h"
 #include "ash/birch/birch_item.h"
+#include "ash/constants/notifier_catalogs.h"
 #include "ash/public/cpp/coral_delegate.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/icon_button.h"
 #include "ash/style/typography.h"
+#include "ash/system/toast/toast_manager_impl.h"
+#include "ash/wm/desks/templates/saved_desk_presenter.h"
 #include "ash/wm/overview/birch/birch_animation_utils.h"
 #include "ash/wm/overview/birch/birch_bar_constants.h"
 #include "ash/wm/overview/birch/birch_bar_controller.h"
@@ -19,6 +22,8 @@
 #include "ash/wm/overview/birch/birch_chip_context_menu_model.h"
 #include "ash/wm/overview/birch/resources/grit/coral_resources.h"
 #include "ash/wm/overview/birch/tab_app_selection_host.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/overview/overview_session.h"
 #include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/cxx23_to_underlying.h"
@@ -89,6 +94,8 @@ constexpr ui::ColorId kSubtitleColorId = cros_tokens::kCrosSysOnSurfaceVariant;
 
 constexpr gfx::Size kLoadingAnimationSize = gfx::Size(100, 20);
 
+constexpr char kMaxSavedGroupsToastId[] = "coral_max_saved_groups_toast";
+
 BirchSuggestionType GetSuggestionTypeFromItemType(BirchItemType item_type) {
   switch (item_type) {
     case BirchItemType::kWeather:
@@ -452,6 +459,23 @@ void BirchChipButton::ExecuteCommand(int command_id, int event_flags) {
       break;
     case base::to_underlying(CommandId::kCoralSaveForLater): {
       CHECK_EQ(BirchItemType::kCoral, item_->GetType());
+
+      // Show a toast if we already have the max amount of allowed coral saved
+      // groups.
+      auto* saved_desk_presenter =
+          OverviewController::Get()->overview_session()->saved_desk_presenter();
+      if (saved_desk_presenter->GetEntryCount(DeskTemplateType::kCoral) >=
+          saved_desk_presenter->GetMaxEntryCount(DeskTemplateType::kCoral)) {
+        ToastData toast(kMaxSavedGroupsToastId,
+                        ToastCatalogName::kCoralSavedGroupLimitMax,
+                        l10n_util::GetStringUTF16(
+                            IDS_ASH_BIRCH_CORAL_SAVED_GROUPS_MAX_NUM_REACHED),
+                        ToastData::kDefaultToastDuration,
+                        /*visible_on_lock_screen=*/false);
+        Shell::Get()->toast_manager()->Show(std::move(toast));
+        return;
+      }
+
       auto* coral_provider = BirchCoralProvider::Get();
       Shell::Get()->coral_delegate()->CreateSavedDeskFromGroup(
           coral_provider->ExtractGroupById(
diff --git a/ash/wm/overview/birch/birch_chip_context_menu_model.cc b/ash/wm/overview/birch/birch_chip_context_menu_model.cc
index a4eaa1e89aefe..56611b4b2c416 100644
--- a/ash/wm/overview/birch/birch_chip_context_menu_model.cc
+++ b/ash/wm/overview/birch/birch_chip_context_menu_model.cc
@@ -81,7 +81,8 @@ BirchChipContextMenuModel::BirchChipContextMenuModel(
       // TODO(zxdan): Localize the strings.
       AddItemWithIcon(base::to_underlying(CommandId::kCoralNewDesk), u"Open",
                       CreateIconForMenuItem(kCoralOpenIcon));
-      if (features::IsCoralSavedDeskFeatureEnabled()) {
+      if (features::IsCoralSavedDeskFeatureEnabled() &&
+          !display::Screen::GetScreen()->InTabletMode()) {
         AddItemWithIcon(base::to_underlying(CommandId::kCoralSaveForLater),
                         u"Save group for later",
                         CreateIconForMenuItem(kSaveDeskForLaterIcon));
diff --git a/components/desks_storage/core/desk_model.h b/components/desks_storage/core/desk_model.h
index 66666b4526347..62c2fb87bd094 100644
--- a/components/desks_storage/core/desk_model.h
+++ b/components/desks_storage/core/desk_model.h
@@ -166,6 +166,9 @@ class DeskModel {
   // Gets the number of desk templates currently saved.
   virtual size_t GetDeskTemplateEntryCount() const = 0;
 
+  // Gets the number of coral saved groups currently saved.
+  virtual size_t GetCoralEntryCount() const = 0;
+
   // Gets the maximum number of save and recall desks entry this storage backend
   // could hold.
   virtual size_t GetMaxSaveAndRecallDeskEntryCount() const = 0;
@@ -174,6 +177,10 @@ class DeskModel {
   // could hold.
   virtual size_t GetMaxDeskTemplateEntryCount() const = 0;
 
+  // Gets the maximum number of coral saved groups this storage backend could
+  // hold.
+  virtual size_t GetMaxCoralEntryCount() const = 0;
+
   // Returns a vector of desk template UUIDs.
   // This method assumes each implementation has a cache and can return the
   // UUIDs synchronously.
diff --git a/components/desks_storage/core/desk_model_wrapper.cc b/components/desks_storage/core/desk_model_wrapper.cc
index fe528705e22c2..d2776e643596a 100644
--- a/components/desks_storage/core/desk_model_wrapper.cc
+++ b/components/desks_storage/core/desk_model_wrapper.cc
@@ -127,6 +127,10 @@ size_t DeskModelWrapper::GetDeskTemplateEntryCount() const {
          policy_entries_.size();
 }
 
+size_t DeskModelWrapper::GetCoralEntryCount() const {
+  return 0u;
+}
+
 size_t DeskModelWrapper::GetMaxSaveAndRecallDeskEntryCount() const {
   return save_and_recall_desks_model_->GetMaxSaveAndRecallDeskEntryCount();
 }
@@ -136,6 +140,10 @@ size_t DeskModelWrapper::GetMaxDeskTemplateEntryCount() const {
          policy_entries_.size();
 }
 
+size_t DeskModelWrapper::GetMaxCoralEntryCount() const {
+  return 0u;
+}
+
 std::set<base::Uuid> DeskModelWrapper::GetAllEntryUuids() const {
   std::set<base::Uuid> keys;
 
diff --git a/components/desks_storage/core/desk_model_wrapper.h b/components/desks_storage/core/desk_model_wrapper.h
index 93a34edf27d09..6087173f4b30b 100644
--- a/components/desks_storage/core/desk_model_wrapper.h
+++ b/components/desks_storage/core/desk_model_wrapper.h
@@ -47,8 +47,10 @@ class DeskModelWrapper : public DeskModel {
   size_t GetEntryCount() const override;
   size_t GetSaveAndRecallDeskEntryCount() const override;
   size_t GetDeskTemplateEntryCount() const override;
+  size_t GetCoralEntryCount() const override;
   size_t GetMaxSaveAndRecallDeskEntryCount() const override;
   size_t GetMaxDeskTemplateEntryCount() const override;
+  size_t GetMaxCoralEntryCount() const override;
   std::set<base::Uuid> GetAllEntryUuids() const override;
   bool IsReady() const override;
   bool IsSyncing() const override;
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index 472df6764c85d..19811282dce49 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -475,6 +475,10 @@ size_t DeskSyncBridge::GetDeskTemplateEntryCount() const {
   return template_count + policy_entries_.size();
 }
 
+size_t DeskSyncBridge::GetCoralEntryCount() const {
+  return 0u;
+}
+
 // Chrome sync does not support save and recall desks yet. Return 0 for max
 // count.
 size_t DeskSyncBridge::GetMaxSaveAndRecallDeskEntryCount() const {
@@ -485,6 +489,10 @@ size_t DeskSyncBridge::GetMaxDeskTemplateEntryCount() const {
   return kMaxTemplateCount + policy_entries_.size();
 }
 
+size_t DeskSyncBridge::GetMaxCoralEntryCount() const {
+  return 0u;
+}
+
 std::set<base::Uuid> DeskSyncBridge::GetAllEntryUuids() const {
   std::set<base::Uuid> keys;
 
diff --git a/components/desks_storage/core/desk_sync_bridge.h b/components/desks_storage/core/desk_sync_bridge.h
index 070e1e97b6f95..6b54444a6f890 100644
--- a/components/desks_storage/core/desk_sync_bridge.h
+++ b/components/desks_storage/core/desk_sync_bridge.h
@@ -74,8 +74,10 @@ class DeskSyncBridge : public syncer::DataTypeSyncBridge, public DeskModel {
   size_t GetEntryCount() const override;
   size_t GetSaveAndRecallDeskEntryCount() const override;
   size_t GetDeskTemplateEntryCount() const override;
+  size_t GetCoralEntryCount() const override;
   size_t GetMaxSaveAndRecallDeskEntryCount() const override;
   size_t GetMaxDeskTemplateEntryCount() const override;
+  size_t GetMaxCoralEntryCount() const override;
   std::set<base::Uuid> GetAllEntryUuids() const override;
   bool IsReady() const override;
   // Whether this sync bridge is syncing local data to sync. This sync bridge
diff --git a/components/desks_storage/core/fake_desk_sync_bridge.cc b/components/desks_storage/core/fake_desk_sync_bridge.cc
index 15e2046911522..33034e1d269a8 100644
--- a/components/desks_storage/core/fake_desk_sync_bridge.cc
+++ b/components/desks_storage/core/fake_desk_sync_bridge.cc
@@ -160,6 +160,10 @@ size_t FakeDeskSyncBridge::GetDeskTemplateEntryCount() const {
   return template_count + policy_entries_.size();
 }
 
+size_t FakeDeskSyncBridge::GetCoralEntryCount() const {
+  return 0u;
+}
+
 // Chrome sync does not support save and recall desks yet. Return 0 for max
 // count.
 size_t FakeDeskSyncBridge::GetMaxSaveAndRecallDeskEntryCount() const {
@@ -170,6 +174,10 @@ size_t FakeDeskSyncBridge::GetMaxDeskTemplateEntryCount() const {
   return 6u + policy_entries_.size();
 }
 
+size_t FakeDeskSyncBridge::GetMaxCoralEntryCount() const {
+  return 0u;
+}
+
 std::set<base::Uuid> FakeDeskSyncBridge::GetAllEntryUuids() const {
   std::set<base::Uuid> keys;
 
diff --git a/components/desks_storage/core/fake_desk_sync_bridge.h b/components/desks_storage/core/fake_desk_sync_bridge.h
index f15ec039af772..ea3ff8ce10888 100644
--- a/components/desks_storage/core/fake_desk_sync_bridge.h
+++ b/components/desks_storage/core/fake_desk_sync_bridge.h
@@ -35,8 +35,10 @@ class FakeDeskSyncBridge : public DeskModel {
   size_t GetEntryCount() const override;
   size_t GetSaveAndRecallDeskEntryCount() const override;
   size_t GetDeskTemplateEntryCount() const override;
+  size_t GetCoralEntryCount() const override;
   size_t GetMaxSaveAndRecallDeskEntryCount() const override;
   size_t GetMaxDeskTemplateEntryCount() const override;
+  size_t GetMaxCoralEntryCount() const override;
   std::set<base::Uuid> GetAllEntryUuids() const override;
   bool IsReady() const override;
   // Whether this sync bridge is syncing local data to sync. This sync bridge
diff --git a/components/desks_storage/core/local_desk_data_manager.cc b/components/desks_storage/core/local_desk_data_manager.cc
index 848934a484f0e..36758e19b5aeb 100644
--- a/components/desks_storage/core/local_desk_data_manager.cc
+++ b/components/desks_storage/core/local_desk_data_manager.cc
@@ -56,7 +56,8 @@ constexpr size_t kMaxCoralDeskCount = 6u;
 
 // Set of valid desk types.
 constexpr auto kValidDeskTypes = base::MakeFixedFlatSet<ash::DeskTemplateType>(
-    {ash::DeskTemplateType::kTemplate, ash::DeskTemplateType::kSaveAndRecall});
+    {ash::DeskTemplateType::kTemplate, ash::DeskTemplateType::kSaveAndRecall,
+     ash::DeskTemplateType::kCoral});
 
 // Reads a file at `fully_qualified_path` into a
 // `ash::DeskTemplate` or as `SavedDeskParseError` code. This function returns a
@@ -398,7 +399,8 @@ void LocalDeskDataManager::DeleteAllEntries(DeleteEntryCallback callback) {
 // TODO(crbug.com/1320805): Remove this function once both desk models support
 // desk type counts.
 size_t LocalDeskDataManager::GetEntryCount() const {
-  return GetSaveAndRecallDeskEntryCount() + GetDeskTemplateEntryCount();
+  return GetSaveAndRecallDeskEntryCount() + GetDeskTemplateEntryCount() +
+         GetCoralEntryCount();
 }
 
 size_t LocalDeskDataManager::GetSaveAndRecallDeskEntryCount() const {
@@ -410,6 +412,10 @@ size_t LocalDeskDataManager::GetDeskTemplateEntryCount() const {
          policy_entries_.size();
 }
 
+size_t LocalDeskDataManager::GetCoralEntryCount() const {
+  return saved_desks_list_.at(ash::DeskTemplateType::kCoral).size();
+}
+
 size_t LocalDeskDataManager::GetMaxSaveAndRecallDeskEntryCount() const {
   return kMaxSaveAndRecallDeskCount;
 }
@@ -418,6 +424,10 @@ size_t LocalDeskDataManager::GetMaxDeskTemplateEntryCount() const {
   return kMaxDeskTemplateCount + policy_entries_.size();
 }
 
+size_t LocalDeskDataManager::GetMaxCoralEntryCount() const {
+  return kMaxCoralDeskCount;
+}
+
 std::set<base::Uuid> LocalDeskDataManager::GetAllEntryUuids() const {
   std::set<base::Uuid> keys;
   for (const auto& type_and_saved_desks : saved_desks_list_) {
diff --git a/components/desks_storage/core/local_desk_data_manager.h b/components/desks_storage/core/local_desk_data_manager.h
index cb97220c752fa..e62f6456b2ec7 100644
--- a/components/desks_storage/core/local_desk_data_manager.h
+++ b/components/desks_storage/core/local_desk_data_manager.h
@@ -105,8 +105,10 @@ class LocalDeskDataManager : public DeskModel, public AdminTemplateModel {
   size_t GetEntryCount() const override;
   size_t GetSaveAndRecallDeskEntryCount() const override;
   size_t GetDeskTemplateEntryCount() const override;
+  size_t GetCoralEntryCount() const override;
   size_t GetMaxSaveAndRecallDeskEntryCount() const override;
   size_t GetMaxDeskTemplateEntryCount() const override;
+  size_t GetMaxCoralEntryCount() const override;
   std::set<base::Uuid> GetAllEntryUuids() const override;
   bool IsReady() const override;
   bool IsSyncing() const override;
diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml
index 1f91b040da51c..898270936496f 100644
--- a/tools/metrics/histograms/metadata/ash/enums.xml
+++ b/tools/metrics/histograms/metadata/ash/enums.xml
@@ -2365,6 +2365,7 @@ chromeos/ash/components/peripheral_notification/peripheral_notification_manager.
   <int value="55" label="OnTask URL blocked"/>
   <int value="56" label="Copy Image To Clipboard Action"/>
   <int value="57" label="Capture Mode Text Copied"/>
+  <int value="58" label="Coral Saved Groups Limit Max"/>
 </enum>
 
 <enum name="TogglePickerAction">