siyuan-note/siyuan · error · Error

window.siyuan.languages.clipboardPermissionDenied

Error message

window.siyuan.languages.clipboardPermissionDenied

What it means

writeClipboardData on HarmonyOS delegates to the native shell via window.JSHarmony.writeHTMLClipboard / writeClipboard. When the shell returns false — meaning the OS denied clipboard write permission — the function throws an Error carrying the localized message window.siyuan.languages.clipboardPermissionDenied.

Solutions

  1. Grant the SiYuan app clipboard permission in the HarmonyOS system settings and retry the copy
  2. Update the Harmony shell (siyuan-harmony) to a version implementing writeHTMLClipboard/writeClipboard correctly
  3. Catch this error in copy actions and show the localized permission-denied message with a hint to re-copy manually
  4. Verify window.JSHarmony exists before calling it and degrade to a plain-text fallback copy path

Example fix

// before
writeClipboardData(protyle, textPlain, textHTML, textSiyuan); // throws on Harmony if denied
// after
try {
  await writeClipboardData(protyle, textPlain, textHTML, textSiyuan);
} catch (e) {
  if (e.message === window.siyuan.languages.clipboardPermissionDenied) {
    showMessage(window.siyuan.languages.clipboardPermissionDenied);
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

const harmonyClipboardAvailable =
  typeof window.JSHarmony !== "undefined" &&
  typeof window.JSHarmony.writeClipboard === "function";

Type guard

const hasHarmonyClipboardBridge = (): boolean =>
  typeof (window as any).JSHarmony !== "undefined" &&
  typeof (window as any).JSHarmony.writeHTMLClipboard === "function";

Try / catch

try {
  await writeClipboardData(protyle, textPlain, textHTML, textSiyuan);
} catch (e) {
  if (e.message === window.siyuan.languages.clipboardPermissionDenied) {
    showMessage(window.siyuan.languages.clipboardPermissionDenied);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Copying editor content (plain or rich) in the HarmonyOS app while the system clipboard permission is denied or the shell bridge returns false — e.g. user revoked clipboard access, the shell is in a state blocking clipboard writes, or an old Harmony shell lacking writeHTMLClipboard support.

Common situations: User denied the clipboard permission prompt in the Harmony app; OS background-clipboard restrictions; using a Harmony shell version without the JSHarmony clipboard bridge methods (bridge undefined / returns false).

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/f97d670e5a87fb2a. Report an issue: GitHub.

Appendix: source

Thrown at app/src/protyle/util/compatibility.ts:433

    const fallbackToPlainText = options.fallbackToPlainText !== false;
    try {
        if (isInAndroid()) {
            if (textSiyuan) {
                window.JSAndroid.writeSiYuanHTMLClipboard(textPlain, textHTML, textSiyuan);
                return {status: "rich"};
            }
            if (textHTML) {
                window.JSAndroid.writeHTMLClipboard(textPlain, textHTML);
                return {status: "rich"};
            }
            window.JSAndroid.writeClipboard(textPlain);
            return {status: "plain"};
        }
        if (isInHarmony()) {
            if (textHTML || textSiyuan) {
                // 使用通用 HTML 注释封装,使旧版鸿蒙壳也不会写入可见的内部格式分隔符。
                if (window.JSHarmony.writeHTMLClipboard(textPlain, buildWebClipboardHTML(textHTML, textSiyuan)) === false) {
                    throw new Error(window.siyuan.languages.clipboardPermissionDenied);
                }
                return {status: "rich"};
            }
            if (window.JSHarmony.writeClipboard(textPlain) === false) {
                throw new Error(window.siyuan.languages.clipboardPermissionDenied);
            }
            return {status: "plain"};
        }
        if (isInIOS()) {
            window.webkit.messageHandlers.setClipboard.postMessage(textPlain || textHTML);
            return {status: "plain"};
        }
        if (textHTML && navigator.clipboard?.write && typeof ClipboardItem !== "undefined") {
            const clipboardItem: Record<string, Blob> = {};
            if (textPlain) {
                clipboardItem["text/plain"] = new Blob([textPlain], {type: "text/plain"});
            }
            const webHTML = buildWebClipboardHTML(textHTML, textSiyuan);

View on GitHub (pinned to 9f775e8a12)