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

Google Script插件插入图片报错‘Action not allowed’排查求助

Google Sheets插件发布版插入Drive图片触发"Action not allowed"错误排查

问题背景

基于Cloud Platform开发Google Sheets插件,流程如下:

  • 用户上传图片,插件将图片保存至用户Drive指定文件夹
  • 日志显示Logo file accessible: imagefilename.png,确认插件可正常访问该图片
  • 插件尝试将图片插入文档时,触发Action not allowed错误

核心代码

function insertLogoIntoDocument(body, userProperties) {
  const functionName = "insertLogoIntoDocument";
  const functionNameError = `${functionName}_error`;
  basicLog(functionName, "LOGO_DEBUG_INIT", "Starting function");

  try {
    // Check if body is valid
    if (!body) {
      basicLog(functionNameError, "LOGO_DEBUG_INVALID_BODY", "Document body is null or undefined");
      return { isError: true, message: "Invalid document body provided" };
    }

    const useCustomLogo = isUsingCustomLogo();
    const logoTextElement = body.findText("<LOGO>");

    if (useCustomLogo) {
      try {
        const logoImageId = userProperties.getProperty(LOGO_IMAGE_ID);
        basicLog(functionName, "LOGO_DEBUG_CUSTOM_DETECTED", "Checking for logo placeholder");

        // Log whether the logo placeholder was found
        basicLog(functionName, "LOGO_DEBUG_SEARCH_RESULT", "Logo placeholder search completed");

        if (logoTextElement) {
          try {
            const logoImage = DriveApp.getFileById(logoImageId);
            const logoBlob = logoImage.getBlob();
            const logoElement = logoTextElement.getElement();
            const logoPosition = logoElement.getParent().getChildIndex(logoElement);
            const logoParagraph = logoElement.getParent();

            basicLog(functionName, "LOGO_DEBUG_CUSTOM_FILE_DATA",
                `Logo ID: ${logoImageId}, File: ${logoImage.getName()}, Blob: ${logoBlob.getBytes().length} bytes, Element: ${logoElement.getType()}, Parent: ${logoParagraph.getType()}, Position: ${logoPosition}`
            );

            logoElement.setText("");
            const inlineImage = logoParagraph.insertInlineImage(logoPosition, logoBlob);

            const originalWidth = inlineImage.getWidth();
            const originalHeight = inlineImage.getHeight();
            const aspectRatio = originalWidth / originalHeight;

            let targetHeight = 72;
            let targetWidth;

            try {
              const savedLogoSize = userProperties.getProperty(LOGO_SIZE_PROPERTY);
              basicLog(functionName, "LOGO_DEBUG_SIZE_RAW", "Processing saved logo size");

              if (savedLogoSize) {
                targetHeight = parseInt(savedLogoSize, 10);
                if (isNaN(targetHeight)) {
                  basicLog(functionName, "LOGO_DEBUG_SIZE_INVALID", "Using default size");
                  targetHeight = 72;
                }
              } else {
                basicLog(functionName, "LOGO_DEBUG_SIZE_MISSING", "Using default size");
                targetHeight = 72;
              }

              basicLog(functionName, "LOGO_DEBUG_SIZE_FINAL", `Height: ${targetHeight}`);
            } catch (err) {
              const errorMessage = err.message || "No error message available";
              const stackTrace = err.stack || "No stack trace available";
              basicLog(functionNameError, "LOGO_DEBUG_SIZE_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
              // Continue with default height if there's an error
              targetHeight = 72;
            }

            targetWidth = Math.round(targetHeight * aspectRatio);
            inlineImage.setHeight(targetHeight);
            inlineImage.setWidth(targetWidth);

            basicLog(functionName, "LOGO_DEBUG_INSERT_SUCCESS", `Height: ${targetHeight}, Width: ${targetWidth}`);
            return { isError: false, message: "Logo inserted successfully" };
          } catch (err) {
            const errorMessage = err.message || "No error message available";
            const stackTrace = err.stack || "No stack trace available";
            basicLog(functionNameError, "LOGO_DEBUG_INSERTION_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
            return { isError: true, message: "Error inserting logo: " + errorMessage };
          }
        } else {
          try {
            const firstParagraph = body.getParagraphs()[0] ? body.getParagraphs()[0].getText() : "No paragraphs";
            const secondParagraph = body.getParagraphs()[1] ? body.getParagraphs()[1].getText() : "No second paragraph";
            basicLog(functionName, "LOGO_DEBUG_PLACEHOLDER_MISSING", `First paragraph: ${firstParagraph}, Second paragraph: ${secondParagraph}`);
            return { isError: true, message: "Logo placeholder not found in document" };
          } catch (err) {
            const errorMessage = err.message || "No error message available";
            const stackTrace = err.stack || "No stack trace available";
            basicLog(functionNameError, "LOGO_DEBUG_TEMPLATE_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
            return { isError: true, message: "Error checking document paragraphs" };
          }
        }
      } catch (err) {
        const errorMessage = err.message || "No error message available";
        const stackTrace = err.stack || "No stack trace available";
        basicLog(functionNameError, "LOGO_DEBUG_HANDLING_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
        return { isError: true, message: "Error handling logo: " + errorMessage };
      }
    } else if (logoTextElement) {
      try {
        body.replaceText("<LOGO>", "");
        basicLog(functionName, "LOGO_DEBUG_PLACEHOLDER_REMOVED", "No custom logo used");
        return { isError: false, message: "Logo placeholder removed successfully" };
      } catch (err) {
        const errorMessage = err.message || "No error message available";
        const stackTrace = err.stack || "No stack trace available";
        basicLog(functionNameError, "LOGO_DEBUG_REMOVAL_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
        return { isError: true, message: "Error removing logo placeholder" };
      }
    }
    
    // Default return if no conditions above were met
    return { isError: false, message: "No logo processing needed" };
  } catch (err) {
    const errorMessage = err.message || "No error message available";
    const stackTrace = err.stack || "No stack trace available";
    basicLog(functionNameError, "LOGO_DEBUG_UNEXPECTED_ERROR", `Error: ${errorMessage}, Stack: ${stackTrace}`);
    return { isError: true, message: "Unexpected error in logo processing" };
  }
}

日志信息

LOGO_DEBUG_CUSTOM_FILE_DATA. Additional Info: Logo ID: "123456...", File: image_name.png, Blob: 158768 bytes, Element: TEXT, Parent: PARAGRAPH, Position: 0.

LOGO_DEBUG_INSERTION_ERROR. Additional Info: 
    Error: Action not allowed, Stack: Exception: Action not allowed
    at insertLogoIntoDocument (reportsGenerate/utils/logoHelper:38:47)

关键信息

  • 已成功获取图片Blob(日志显示Blob字节数正常)
  • 仅发布版报错,测试部署(Test Deployments)环境运行正常
  • 已确认权限范围正确,且仅登录单个Google账号
  • 尝试多种插入方式仍未解决

排查思路

  • 核对发布版OAuth权限配置:测试部署与正式发布的权限范围可能存在差异,重新检查发布版OAuth consent screen的权限列表,确保https://www.googleapis.com/auth/drive.file或对应权限已添加并标记为必要权限;确认用户在授权流程中已完整授予Drive相关权限。
  • 验证文件与文档的权限上下文:检查目标文档和Drive图片的共享设置,确认插件运行的用户身份对两者均有编辑权限;可通过Drive API获取图片权限信息,验证插件身份的访问权限。
  • 排查Workspace Marketplace限制:若插件通过Google Workspace Marketplace发布,查看控制台的审核状态与通知,确认是否触发了正式发布后的安全限制。
  • 调整图片插入逻辑:尝试替换insertInlineImage为appendImage等其他方法;调整操作顺序,先插入图片再清空占位符文本;或尝试将Blob转为Base64后插入,绕过直接引用Drive文件的限制。
  • 确认发布版代码与执行环境:核对发布版代码与测试通过的代码是否完全一致;添加更详细的上下文日志,记录当前用户ID、文档ID、文件ID的权限状态,确认运行时身份正确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:34:53