You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gmail插件无法创建回复草稿:权限错误求助

Fix: "Access denied: Cannot compose without user interaction" in Gmail Add-on

Let's break down what's causing this error and how to fix it:

The Root Cause

Your current setup uses an unconditional contextual trigger that automatically runs createReplyDraft when the add-on loads. However, Gmail's Compose Actions (like creating a reply draft) require explicit user interaction (e.g., clicking a button) to execute—they can't be triggered automatically in the background. This mismatch is why you're seeing the "Cannot compose without user interaction" error.

Step-by-Step Fix

1. Update Your appsscript.json Configuration

Change the contextual trigger's onTriggerFunction to a new function that will render a card with your "Compose Reply" button (instead of directly running the draft creation logic):

{
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute",
    "https://www.googleapis.com/auth/gmail.addons.current.action.compose"
  ],
  "gmail": {
    "name": "Gmail Add-on Quickstart",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/2x/bookmark_black_24dp.png",
    "contextualTriggers": [{
      "unconditional": {},
      "onTriggerFunction": "showComposeButton" // Updated function name
    }],
    "openLinkUrlPrefixes": [ "https://mail.google.com/" ],
    "primaryColor": "#4285F4",
    "secondaryColor": "#4285F4"
  }
}

2. Modify Your Code.gs File

Add a new function showComposeButton that builds and returns a card containing your "Compose Reply" button. This function will be triggered by the contextual trigger to display the button, and clicking the button will then run createReplyDraft:

// New function to render the card with the compose button
function showComposeButton() {
  var composeAction = CardService.newAction().setFunctionName('createReplyDraft');
  var composeButton = CardService.newTextButton()
    .setText('Compose Reply')
    .setComposeAction(composeAction, CardService.ComposedEmailType.REPLY_AS_DRAFT);
  
  var cardSection = CardService.newCardSection().addWidget(composeButton);
  var card = CardService.newCardBuilder().addSection(cardSection).build();
  
  return [card];
}

/**
 * Creates a draft email (with an attachment and inline image)
 * as a reply to an existing message.
 * @param {Object} e data passed by the compose action.
 * @return {ComposeActionResponse}
 */
function createReplyDraft(e) {
  // Activate temporary Gmail add-on scopes, in this case to allow
  // a reply to be drafted.
  var accessToken = e.messageMetadata.accessToken;
  GmailApp.setCurrentMessageAccessToken(accessToken);

  // Creates a draft reply.
  var messageId = e.messageMetadata.messageId;
  var message = GmailApp.getMessageById(messageId);
  var draft = message.createDraftReply('', { htmlBody: "Kitten!" } );

  // Return a built draft response. This causes Gmail to present a
  // compose window to the user, pre-filled with the content specified
  // above.
  return CardService.newComposeActionResponseBuilder()
    .setGmailDraft(draft).build();
}

Why This Works

  • The contextual trigger now runs showComposeButton, which only renders a card with a button—no automatic draft creation happens here.
  • When the user clicks the "Compose Reply" button, that explicit interaction triggers createReplyDraft, which is allowed to generate the draft and open the compose panel.

Additional Notes

  • You don't need the full https://mail.google.com/ scope for this functionality—your original scopes are sufficient.
  • After making these changes, redeploy your add-on and test it by opening an email in Gmail: you'll see your add-on's card with the button, and clicking it will correctly open the reply panel with your pre-filled content.

内容的提问来源于stack exchange,提问作者suyogricha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:58:27