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

使用ProjectApp2库批量创建GAS项目时出现Invalid ID错误排查

批量创建GAS项目时触发404 Invalid ID错误排查

问题背景

此前脚本运行完全正常,近一周开始报错。ProjectApp2库的更新项目、获取项目信息功能均正常,但在批量创建Google Apps Script(GAS)项目时触发404错误。测试脚本可正常运行,但主脚本执行时始终报错。

主脚本代码

/**
 * Создает проекты Google Apps Script из исходного кода, представленного в виде блобов (бинарных объектов).
 * Проекты создаются на основе данных, взятых из определенного листа.
 */
function создать_проекты_из_блоб_файлов__createProjectByBlob() {
  // Получение строк с названиями файлов и исходным кодом из листа SheetProject.
  // Исключение заголовков и пустых строк.
  const rows = Utils.values(SheetProject).filter(row => row[0] && row[0] != 'fileName');

  // Формирование массива объектов с информацией о файлах скрипта.
  const scriptFilesInfo = rows.map(row => ({ name: row[0].toString(), code: row[1].toString() }));

  // Перевод информации о файлах скрипта в объекты Blob. 
  const blobs = scriptFilesInfo.map(({ name, code }) =>
    Utilities.newBlob(code, 'text/plain', name));

  try {
    // Получение имен проектов для создания из листа, исключение заголовков и пустых строк.
    const data = Utils.values(SheetProject).filter(row => row[2] && row[2] !== 'projectName').map(row => row[2]);

    // Создание проектов с использованием функции ProjectApp2.createProjectByBlob
    // и данных о файлах скрипта.
    const creations = data.map((projectName) => {

      const projectCreationResult = ProjectApp2.createProjectByBlob(projectName, blobs);
      console.log(projectCreationResult);
      // Возвращаем объект с информацией о созданном проекте.
      return {
        url: `https://script.google.com/u/0/home/projects/${projectCreationResult.scriptId}/edit`,
        name: projectCreationResult.name,
        result: projectCreationResult
      };
    });

    // Логирование результатов создания проектов.
    console.log('Created projects:', JSON.stringify(creations, null, 2));

    // Отображение результатов создания проектов пользователя.
    show(creations.map(({ url, name }) => [url, name]));
  } catch (error) {
    // Логирование ошибок в случае их возникновения.
    console.error('Error in createProjectByBlob function:', JSON.stringify({ error: error.toString() }, null, 2));
  }
};

错误信息

Error: {"error":{"code":404,"message":"Invalid ID","status":"NOT_FOUND","details":[{"@type":"type.googleapis.com/google.rpc.LocalizedMessage","locale":"en-US","message":"Invalid ID"}]}}
updateProject   @ Code.gs:218
createProject   @ Code.gs:192
ProjectApp2.createProjectByBlob @ Code.gs:106
createProjectByBlob @ Code.gs:30
(функция без названия)  @ testing.gs:24

测试脚本代码

function myFunction() {
  var json = {
    timeZone: "Asia/Tokyo",
    dependencies: {
      enabledAdvancedServices: [
        {
          userSymbol: "Drive",
          serviceId: "drive",
          version: "v2",
        },
      ],
    },
    exceptionLogging: "STACKDRIVER",
    oauthScopes: ["https://www.googleapis.com/auth/drive"],
  };

  const names = ['ghbdtn', 'gjrf', 'ljcdblfybz'];
  var blob = Utilities.newBlob(Utils.stringify(json), 'text/plain', "appsscript.json");
  var blobs = [blob, Utilities.newBlob('// тесты', 'text/plain', 'ntcn')];
  var res = names.map(name => ProjectApp2.createProjectByBlob(name, blobs))
  // var res = ProjectApp2.updateProjectByBlob(projectId, blob); // If this is used, the Manifests in the existing project is updated.
  Logger.log(res);

}

scriptFilesInfo示例

[ { name: 'appsscript.json',
    code: '{
  "timeZone": "Asia/Yekaterinburg",
  "dependencies": {
    "libraries": [
      {
        "userSymbol": "Trigger_lib",
        "libraryId": "1JqffuBy8rTgiUSudJYAbR8qokG-3MUSRClmyWRrY5S8",
        "version": 0,
        "developmentMode": true
      },
      {
        "userSymbol": "Tg_lib",
        "libraryId": "1UbzKANMiHPB7PImDQDEXmJygTlvlDVSRe-kUV68JgTE",
        "version": 0,
        "developmentMode": true
      }
    ]
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.scriptapp",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/drive.file",
    "https://www.googleapis.com/auth/drive.readonly",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/spreadsheets.readonly",
    "https://mail.google.com/",
    "https://www.google.com/calendar/feeds",
    "https://www.google.com/m8/feeds",
    "https://www.googleapis.com/auth/admin.directory.group",
    "https://www.googleapis.com/auth/admin.directory.user",
    "https://www.googleapis.com/auth/documents",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/forms.currentonly",
    "https://www.googleapis.com/auth/groups",
    "https://www.googleapis.com/auth/script.deployments",
    "https://www.googleapis.com/auth/script.deployments.readonly",
    "https://www.googleapis.com/auth/script.metrics",
    "https://www.googleapis.com/auth/script.processes",
    "https://www.googleapis.com/auth/script.projects",
    "https://www.googleapis.com/auth/script.projects.readonly",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/userinfo.email"
  ],
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}' },
  { name: 'settings',
    code: '/** 
* постоянные перменные
*/

const ss = SpreadsheetApp.openbyUrl();
const ssID = ss.getId();

// Получение доступа к свойствам скрипта
const properties = PropertiesService.getScriptProperties();

/**
* вспомогательные функции
*/
' } ]

排查关键点

  1. 验证外部库ID有效性
    主脚本的appsscript.json引用了两个第三方库,测试脚本无外部库依赖。错误栈显示在updateProject步骤失败,大概率是库ID无效(库被删除、权限变更或ID拼写错误)。手动在GAS编辑器中添加这两个库,确认是否能正常加载。

  2. 修复代码语法错误
    settings文件中SpreadsheetApp.openbyUrl();存在拼写错误,正确写法是openByUrl()(大写B),且缺少URL参数。这个语法错误会导致项目创建后初始化失败,返回无效ID。

  3. 添加请求延迟避免限流
    Google API有请求频率限制,批量创建项目时连续调用可能触发限流。测试脚本仅创建3个项目,主脚本创建数量更多时容易触发。在循环中添加Utilities.sleep(1000),每次创建后暂停1秒。

  4. 优化表格数据读取逻辑
    主脚本两次调用Utils.values(SheetProject)读取表格数据,可能存在数据不一致问题(比如读取期间表格被修改)。建议只读取一次数据,复用结果:

    // 优化后的数据读取
    const allRows = Utils.values(SheetProject);
    const rows = allRows.filter(row => row[0] && row[0] != 'fileName');
    const data = allRows.filter(row => row[2] && row[2] !== 'projectName').map(row => row[2]);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:44:53