From 190d83564a2399cee478efd1912962ba4da63ef4 Mon Sep 17 00:00:00 2001 From: Andrew Polk Date: Tue, 23 Jun 2026 09:13:51 -0700 Subject: [PATCH] Fix BL-16459 Copying Text Failed https://issues.bloomlibrary.org/youtrack/issue/BL-16459 The common/clipboardText endpoint copies/pastes text via the Windows clipboard. When another program is momentarily holding the clipboard, Windows throws System.Runtime.InteropServices.ExternalException ("Requested Clipboard operation did not succeed"). This is transient and external to Bloom (the underlying Clipboard.SetText overload already retries), but the handler reported it with ModalIf.All, popping a scary "report this problem" dialog the user can do nothing about but retry. Now both the copy (SetText) and paste (GetText) paths catch ExternalException separately and downgrade to a gentle, non-modal toast (ModalIf.None/PassiveIf.All, showSendReport: false) via a new ReportClipboardBusy helper. It still logs and reports to Sentry so we can track frequency. Any other unexpected exception keeps the existing modal report. The toast reuses the existing localized EditTab.Image.CopyImageFailed string already used by EditingView.CopyImageToClipboard's ExternalException handler (though that handler uses a modal MessageBox and does not report to Sentry). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/BloomExe/web/controllers/CommonApi.cs | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/BloomExe/web/controllers/CommonApi.cs b/src/BloomExe/web/controllers/CommonApi.cs index 809cdeab9916..350e46a838b1 100644 --- a/src/BloomExe/web/controllers/CommonApi.cs +++ b/src/BloomExe/web/controllers/CommonApi.cs @@ -4,6 +4,7 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; using Bloom.Api; @@ -118,6 +119,13 @@ public void RegisterWithApiHandler(BloomApiHandler apiHandler) { result = PortableClipboard.GetText(); } + catch (ExternalException e) + { + // The clipboard was momentarily locked by some other program (BL-16459). + // This is transient and not Bloom's fault, so we don't want a scary + // problem-report dialog; just a gentle toast. + ReportClipboardBusy(e); + } catch (Exception e) { // Need to make sure to handle exceptions. @@ -149,6 +157,13 @@ public void RegisterWithApiHandler(BloomApiHandler apiHandler) { PortableClipboard.SetText(content); } + catch (ExternalException e) + { + // The clipboard was momentarily locked by some other program (BL-16459). + // This is transient and not Bloom's fault, so we don't want a scary + // problem-report dialog; just a gentle toast. + ReportClipboardBusy(e); + } catch (Exception e) { // Need to make sure to handle exceptions. @@ -269,6 +284,27 @@ private void HandleInstanceInfo(ApiRequest request) ); } + /// + /// Sometimes a clipboard copy/paste fails because another program is momentarily holding + /// the Windows clipboard (BL-16459). Windows reports this as an ExternalException. It is + /// transient and not caused by Bloom, so rather than alarm the user with a problem-report + /// dialog we just show a gentle toast (and still log/send to Sentry so we can track it). + /// + private static void ReportClipboardBusy(ExternalException e) + { + NonFatalProblem.Report( + ModalIf.None, + PassiveIf.All, + LocalizationManager.GetDynamicString( + "Bloom", + "EditTab.Image.CopyImageFailed", + "Bloom had problems using your computer's clipboard. Some other program may be interfering." + ), + exception: e, + showSendReport: false + ); + } + /// /// Whether any modifications to the current book may currently be saved. /// This is used by many things that don't otherwise need to know about Team Collections,