如何解决 Google Apps Script 开发中的常见错误 [英] How to solve common errors in Google Apps Script development

查看:17
本文介绍了如何解决 Google Apps Script 开发中的常见错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问答目前是 meta 讨论,请参与.目前的计划是在可能的情况下拆分为问答环节.A&A 的答案是社区维基,当状态得到解决时,问题应该成为一个问题.


前言

本问答力求成为Google Apps Script语言开发过程中遇到的常见错误的集合和参考对象,希望能提高 标签的问题.

在其他语言和通用标签中有几个类似和成功的经历(参见 c++, androidphp再次使用php),下面是这个西装.


它为什么存在?

对于开发和生产过程中遇到的错误的含义和解决方案,新老开发人员提出的问题数量非常多,这些问题可以有效地简化为一个单一的答案.在撰写本文时,即使仅通过语言标签运行查询也会产生:

  • "找不到方法"8 页
  • 无法读取属性"9 页
  • 在这种情况下不能调用 ...";5 页
  • 您没有权限";11 页

对于志愿者来说,链接到最相关的副本既困难又耗时,因为需要考虑细微差别和通常措辞不当的标题.


它由什么组成?

此问答包含的条目旨在提供有关如何:

  • 解析错误信息结构
  • 了解错误的含义
  • 持续复制(如适用)
  • 解决问题
  • 提供规范问答的链接(在可能的情况下)

目录

为了帮助您浏览不断增加的参考资料,请使用下面的目录:

  1. 一般错误
  2. 服务特定错误


这不是什么?

问答的范围仅限于普通(并非无关紧要).这不是:

  • 包罗万象的指南或最佳实践";收藏
  • 一般 ECMAScript 错误的参考
  • GAS 文档
  • 资源列表(我们有一个标签维基)

要添加什么?

添加条目时,请考虑以下事项:

  • 错误是否足够常见(参见为什么"部分的示例)?
  • 能否简明扼要地描述解决方案并适用于大多数情况?

解决方案

前言

答案提供了有关使用任何 Google 服务(内置和高级)或 API 时可能遇到的一般错误的指南.有关特定于某些服务的错误,请参阅其他答案.

返回参考


一般错误


留言

<块引用>

TypeError: 无法从 undefined (or null)

读取属性 'property name here'

说明

该错误消息表明您正在尝试访问 Object 实例上的属性,但在运行时期间,值实际上由变量是一种特殊的数据类型undefined.通常,在访问对象的嵌套属性时会发生错误.

此错误的变体用数值代替属性名称表示需要 Array 的实例.由于 JavaScript 中的数组是对象,所以一切这里提到的关于他们也是真实的.

有一种动态构造对象的特殊情况,例如事件对象 仅在特定上下文中可用,例如向应用程序发出 HTTP 请求或通过时间或基于事件的触发器调用函数.

<块引用>

错误是TypeError因为需要 object",但接收到 undefined"

如何修复

  1. 使用默认值
    逻辑或 || 运算符有一个有趣的属性,即如果左侧是 .由于 JS 中的对象是真值,而 undefinednull 是假的,所以像 (myVar || {}).myProp [ 这样的表达式>(myVar || [])[index] for arrays] 将保证不会抛出错误并且属性至少是 undefined.

    还可以提供默认值:(myVar || { myProp : 2 }) 保证访问 myProp 以返回 2默认情况下.数组也是如此:(myVar || [1,2,3]).

  2. 检查类型
    对于特殊情况尤其如此,typeof operator 结合 if 声明比较运算符 将允许函数在其指定的上下文之外运行(即用于调试目的)或根据对象是否存在引入分支逻辑.

    可以控制检查的严格程度:

  • lax ("not undefined"): if(typeof myVar !== "undefined") {//do something;}
  • strict(仅限正确的对象"):if(typeof myVar ===object"&& myVar) {//做事}

相关问答

  1. 解析顺序作为源的GAS项目问题


留言

<块引用>

无法将某些值转换为数据类型

说明

由于传递了一个与方法预期的类型不同的参数,因此抛出错误.导致错误的一个常见错误是意外强制数字到字符串.

如何复制

function testConversionError() {const ss = SpreadsheetApp.getActiveSheet();ss.getRange("42.0",1);}

如何修复

确保错误消息中引用的值是文档和根据需要转换.


留言

<块引用>

无法从此上下文调用服务和方法名称

说明

此错误发生在上下文不匹配并且特定于 容器绑定脚本.导致错误的主要用例是尝试调用仅在一种文档类型中可用的方法(通常是 getUi(),因为它是 由多个服务共享)来自另一个(即 DocumentApp.getUi() 来自电子表格).

次要但也是突出的情况是调用服务的结果 未明确允许自定义函数(通常是由特殊的JSDoc风格注释@customfunction标记并用作公式的函数)调用.

如何复制

对于绑定脚本上下文不匹配,请在与 Google 表格(或 Google 文档以外的任何内容)相关联的脚本项目中声明并运行此函数:

function testContextMismatch() {const doc = DocumentApp.getUi();}

请注意,调用 DocumentApp.getActiveDocument() 只会在不匹配时导致 null,并且执行将会成功.

对于自定义函数,在任何单元格中使用下面声明的函数作为公式:

/*** @自定义函数*/函数 testConversionError() {const ui = SpreadsheetApp.getUi();ui.alert(`UI 超出自定义函数的范围`);}

如何修复

  1. 通过更改调用方法的服务可以轻松修复上下文不匹配.
  2. 自定义函数无法调用这些服务,请使用自定义菜单或对话框.


留言

<块引用>

找不到方法这里的方法名称

<块引用>

参数param namesmethod name

的方法签名不匹配

说明

这个错误对于新手来说是一个众所周知的令人困惑的信息.它说的是在调用相关方法时传递的一个或多个参数中发生了类型不匹配.

<块引用>

签名 没有方法与您的称呼相对应,因此";未找到

如何修复

这里唯一的解决方法是仔细阅读文档并检查顺序和推断类型 的参数是正确的(使用具有自动完成功能的良好 IDE 会有所帮助).但是,有时会出现问题,因为人们期望值是某种类型,而在运行时它是另一种类型.有几个技巧可以防止此类问题:

  1. 设置类型保护(typeof myVar === "string" 和类似的).
  2. 添加验证器以动态修复类型,这要归功于 JavaScript 动态类型.

示例

/*** @summary 纯 arg 验证器样板* @param {function (any) : any}* @param {...any} 参数* @returns {any[]}*/const validate = (guard, ...args) =>args.map(警卫);const functionWithValidator = (...args) =>{const 守卫 = (arg) =>typeof arg !== "number" ?parseInt(arg) : arg;const [a,b,c] = validate(guard, ...args);const asObject = { a, b, c };控制台日志(作为对象);返回对象;};//驱动程序IIFE(() => {functionWithValidator("1 个苹果",2,"0x5");})()


消息

<块引用>

您无权执行该操作

<块引用>

脚本无权执行该操作

说明

该错误表明所访问的 API 或服务之一缺乏来自用户的足够权限.每个在其文档中具有授权部分的服务方法都至少需要授权一个范围.

由于 GAS 本质上围绕 Google API 以方便开发,因此 OAuth 2.0 中列出的大部分范围都适用于 API 参考,但如果在相应的文档中列出了,最好使用它,因为存在一些不一致之处.

请注意,自定义函数会在未经授权的情况下运行.从 Google 工作表单元格调用函数是导致此错误的最常见原因.

如何修复

如果从脚本编辑器运行调用服务的函数,系统会自动提示您使用相关范围对其进行授权.尽管对于快速手动测试很有用,但最佳做法是明确设置范围 在应用程序清单 (appscript.json) 中.此外,自动范围通常太广泛而无法通过审查,如果打算发布应用.

清单文件中的字段 oauthScopes(View -> Show manifest file 如果在代码编辑器中)应如下所示:

 oauthScopes":[https://www.googleapis.com/auth/script.container.ui",https://www.googleapis.com/auth/userinfo.email",//等等]

对于自定义函数,您可以通过切换到从菜单或按钮调用函数来修复它因为不能授权自定义功能.

对于那些正在开发编辑器附加组件,此错误意味着未处理的授权生命周期模式:如果身份验证模式为 AuthMode.NONE,则必须在调用需要授权的服务之前中止.

相关原因和解决方案

  1. @OnlyCurrentDoc 限制脚本访问范围
  2. 范围自动检测


留言

<块引用>

ReferenceError: service name 未定义

说明

最常见的原因是使用高级服务而未启用它.启用此类服务​​时,指定标识符下的变量将附加到 全局范围 开发者可以直接引用.因此,当引用禁用的服务时,会抛出 ReferenceError.

如何修复

转到资源"->高级 Google 服务"菜单并启用引用的服务.请注意,标识符应等于引用的全局变量.如需更详细的说明,请阅读官方指南.

<块引用>

如果没有引用任何高级服务,则错误指向一个未声明的变量被引用.


留言

<块引用>

脚本完成但没有返回任何东西.

<块引用>

未找到脚本函数:doGet 或 doPost

说明

这本身不是错误(因为返回的 HTTP 响应代码是 200 并且执行被标记为成功,但通常认为是一次.尝试发出请求时出现该消息/access from browser a script 部署为网络应用.

发生这种情况的主要原因有两个:

  1. 没有doGetdoPost 触发函数
  2. 上面的触发器不返回HtmlOutputTextOutput 实例

如何修复

对于第一个原因,只需提供 doGetdoPost 触发器(或两者)函数.第二,确保您的应用程序的所有路由都以创建 TextOutputhtml输出:

//doGet 返回 HTML函数 doGet(e) {return HtmlService.createHtmlOutput("<p>Some text</p>");}//doPost 返回文本函数 doPost(e) {const { 参数 } = e;const echoed = JSON.stringify(parameters);返回 ContentService.createTextOutput(echoed);}

请注意,应该声明只有一个触发器函数 - 将它们视为应用程序的入口点.

如果触发器依赖 parameter/parameters 来路由响应,请确保请求 URL 的结构为baseURL/exec?查询"或baseURL/dev?query";其中 query 包含 要传递的参数.

相关问答

  1. 在声明触发器后重新部署


留言

<块引用>

很抱歉,发生服务器错误.请稍等,然后重试.

说明

这是最神秘的错误,几乎可以在任何服务的任何时候发生(尽管 DriveApp 使用特别容易受到它的影响).该错误通常表明 Google 方面存在问题,该问题要么在几个小时/几天内消失,要么在此过程中得到修复.

如何修复

没有灵丹妙药,通常,除了提交问题 在问题跟踪器上或联系支持人员(如果您有 GSuite 帐户).在此之前,您可以尝试以下常见的补救措施:

  1. 对于绑定 脚本 - 创建新文档并复制现有文档项目和数据.
  2. 改用高级Drive 服务(始终记得先启用它).
  3. 可能有一个 正则表达式有问题 如果错误指向一行.

<块引用>

不要因为这个错误而生气 - 尝试定位受影响的代码、文件或为问题加注星标,然后继续


没有明显问题的语法错误

此错误很可能是由于在使用已弃用的 V8 运行时(在编写 GAS 平台时使用 ES6 语法(例如,箭头函数)引起的)使用 V8).

如何修复

打开appscript.json"manifest 文件并检查 runtimeVersion 是否设置为 "V8",如果不是,请更改它,或者删除任何 ES6 特性 否则.


与配额相关的错误

存在几个与对服务使用施加的配额相关的错误.Google 有一个综合列表,但作为一般根据经验,如果邮件与太多" 模式匹配,则您很可能已经超出了相应的配额.

最可能遇到的错误:

  • 服务调用次数过多:service name
  • 运行的脚本过多
  • 一天使用过多计算机时间的服务
  • 这个脚本有太多触发器

如何修复

在大多数情况下,唯一的解决方法是等到配额刷新或切换到另一个帐户(除非将脚本部署为具有以我的身份运行"权限的 Web 应用程序,在这种情况下所有者的 配额将在所有用户之间共享).

引用当时的文档:

<块引用>

每日配额在 24 小时窗口结束时刷新;但是,此刷新的确切时间因用户而异.

请注意,某些服务(例如 MailApp)具有诸如 getRemainingDailyQuota 可以查看剩余配额.

如果超过触发器的最大数量,可以通过getProjectTriggers()(或检查 我的触发器" 选项卡)并相应地减少数量(例如,通过使用 deleteTrigger(trigger) 去掉一些.

相关规范问答

  1. 如何应用每日限制以及刷新了吗?
  2. 超过最大执行时间"问题
  3. 优化服务调用以减少执行时间莉>


参考文献

  1. 如何出错更有意义的信息
  2. 调试自定义函数

The Q&A is currently a subject of meta discussion, do participate. The current plan is to split where possible into Q&As. Answers to the A&A are community wiki and the question should become one when the status is resolved.


Preface

This Q&A strives to become a collection and a reference target for common errors encountered during development in Google Apps Script language in hopes to improve long-term maintainability of tag.

There are several similar and successful undergoings in other languages and general-purpose tags (see c++, android, php, php again), and this one follows suit.


Why it exists?

The amount of questions from both new and experienced developers regarding the meaning and solutions to errors encountered during development and production that can be effectively reduced to a single answer is substantial. At the time of writing, even running a query only by language tag yields:

Linking to a most relevant duplicate is hard and time-consuming for volunteers due to the need to consider nuances and often poorly-worded titles.


What it consists of?

Entries in this Q&A contain are designed to provide info on how to:

  • parse the error message structure
  • understand what the error entails
  • consistently reproduce (where applicable)
  • resolve the issue
  • provide a link to canonical Q&A (where possible)

Table of Contents

To help you navigate the growing reference please use the TOC below:

  1. General errors
  2. Service-specific errors


What this is not?

The scope of the Q&A is limited to common (not trivial). This is not:

  • a catch-all guide or "best practices" collection
  • a reference for general ECMAScript errors
  • GAS documentation
  • a resources list (we have a tag wiki for that)

What to add?

When adding an entry, please, consider the following:

  • is the error common enough (see "why" section for examples)?
  • can the solution be described concisely and be applicable for most cases?

解决方案

Preface

The answer provides a guide on general errors that can be encountered when working with any Google service (both built-in and advanced) or API. For errors specific to certain services, see the other answer.

Back to reference


General errors


Message

TypeError: Cannot read property 'property name here' from undefined (or null)

Description

The error message indicates that you are trying to access a property on an Object instance, but during runtime the value actually held by a variable is a special data type undefined. Typically, the error occurs when accessing nested properties of an object.

A variation of this error with a numeric value in place of property name indicates that an instance of Array was expected. As arrays in JavaScript are objects, everything mentioned here is true about them as well.

There is a special case of dynamically constructed objects such as event objects that are only available in specific contexts like making an HTTP request to the app or invoking a function via time or event-based trigger.

The error is a TypeError because an "object" is expected, but "undefined" is received

How to fix

  1. Using default values
    Logical OR || operator in JavaScript has an intersting property of evaluating the right-hand side iff the left-hand is falsy. Since objects in JS are truthy, and undefined and null are falsy, an expression like (myVar || {}).myProp [(myVar || [])[index] for arrays] will guarantee that no error is thrown and the property is at least undefined.

    One can also provide default values: (myVar || { myProp : 2 }) guarantees accessing myProp to return 2 by default. Same goes for arrays: (myVar || [1,2,3]).

  2. Checking for type
    Especially true for the special case, typeof operator combined with an if statement and a comparison operator will either allow a function to run outside of its designated context (i.e. for debugging purposes) or introduce branching logic depending on whether the object is present or not.

    One can control how strict the check should be:

  • lax ("not undefined"): if(typeof myVar !== "undefined") { //do something; }
  • strict ("proper objects only"): if(typeof myVar === "object" && myVar) { //do stuff }

Related Q&As

  1. Parsing order of the GAS project as the source of the issue


Message

Cannot convert some value to data type

Description

The error is thrown due to passing an argument of different type than a method expects. A common mistake that causes the error is accidental coercion of a number to string.

How to reproduce

function testConversionError() {
  const ss = SpreadsheetApp.getActiveSheet();
  ss.getRange("42.0",1);
}

How to fix

Make sure that the value referenced in the error message is of data type required by documentation and convert as needed.


Message

Cannot call Service and method name from this context

Description

This error happens on a context mismatch and is specific to container-bound scripts. The primary use case that results in the error is trying to call a method only available in one document type (usually, getUi() as it is shared by several services) from another (i.e. DocumentApp.getUi() from a spreadsheet).

A secondary, but also prominent case is a result of calling a service not explicitly allowed to be called from a custom function (usually a function marked by special JSDoc-style comment @customfunction and used as a formula).

How to reproduce

For bound script context mismatch, declare and run this function in a script project tied to Google Sheets (or anything other than Google Docs):

function testContextMismatch() {
  const doc = DocumentApp.getUi();
}

Note that calling a DocumentApp.getActiveDocument() will simply result in null on mismatch, and the execution will succeed.

For custom functions, use the function declared below in any cell as a formula:

/**
 * @customfunction
 */
function testConversionError() {
  const ui = SpreadsheetApp.getUi();
  ui.alert(`UI is out of scope of custom function`);
}

How to fix

  1. Context mismatch is easily fixed by changing the service on which the method is called.
  2. Custom functions cannot be made to call these services, use custom menus or dialogs.


Message

Cannot find method Method name here

The parameters param names do not match the method signature for method name

Description

This error has a notoriously confusing message for newcomers. What it says is that a type mismatch occurred in one or more of the arguments passed when the method in question was called.

There is no method with the signature that corresponds to how you called it, hence "not found"

How to fix

The only fix here is to read the documentation carefully and check if order and inferred type of parameters are correct (using a good IDE with autocomplete will help). Sometimes, though, the issue happens because one expects the value to be of a certain type while at runtime it is of another. There are several tips for preventing such issues:

  1. Setting up type guards (typeof myVar === "string" and similar).
  2. Adding a validator to fix the type dynamically thanks to JavaScript being dynamically typed.

Sample

/**
 * @summary pure arg validator boilerplate
 * @param {function (any) : any}
 * @param {...any} args
 * @returns {any[]}
 */
const validate = (guard, ...args) => args.map(guard);

const functionWithValidator = (...args) => {
  const guard = (arg) => typeof arg !== "number" ? parseInt(arg) : arg;

  const [a,b,c] = validate(guard, ...args);
  
  const asObject = { a, b, c };
  
  console.log(asObject);
  
  return asObject;
};

//driver IIFE
(() => {
  functionWithValidator("1 apple",2,"0x5");
})()


Messages

You do not have permission to perform that action

The script does not have permission to perform that action

Description

The error indicates that one of the APIs or services accessed lacks sufficient permissions from the user. Every service method that has an authorization section in its documentation requires at least one of the scopes to be authorized.

As GAS essentially wraps around Google APIs for development convenience, most of the scopes listed in OAuth 2.0 scopes for APIs reference can be used, although if one is listed in the corresponding docs it may be better to use it as there are some inconsistencies.

Note that custom functions run without authorization. Calling a function from a Google sheet cell is the most common cause of this error.

How to fix

If a function calling the service is ran from the script editor, you are automatically prompted to authorize it with relevant scopes. Albeit useful for quick manual tests, it is best practice to set scopes explicitly in application manifest (appscript.json). Besides, automatic scopes are usually too broad to pass the review if one intends to publish the app.

The field oauthScopes in manifest file (View -> Show manifest file if in code editor) should look something like this:

  "oauthScopes": [
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/userinfo.email",
    //etc
  ]

For custom functions, you can fix it by switching to calling the function from a menu or a button as custom functions cannot be authorized.

For those developing editor Add-ons, this error means an unhandled authorization lifecycle mode: one has to abort before calls to services that require authorization in case auth mode is AuthMode.NONE.

Related causes and solutions

  1. @OnlyCurrentDoc limiting script access scope
  2. Scopes autodetection


Message

ReferenceError: service name is not defined

Description

The most common cause is using an advanced service without enabling it. When such a service is enabled, a variable under the specified identifier is attached to global scope that the developer can reference directly. Thus, when a disabled service is referenced, a ReferenceError is thrown.

How to fix

Go to "Resources -> Advanced Google Services" menu and enable the service referenced. Note that the identifier should equal the global variable referenced. For a more detailed explanation, read the official guide.

If one hasn't referenced any advanced services then the error points to an undeclared variable being referenced.


Message

The script completed but did not return anything.

Script function not found: doGet or doPost

Description

This is not an error per se (as the HTTP response code returned is 200 and the execution is marked as successful, but is commonly regarded as one. The message appears when trying to make a request/access from browser a script deployed as a Web App.

There are two primary reasons why this would happen:

  1. There is no doGet or doPost trigger function
  2. Triggers above do not return an HtmlOutput or TextOutput instance

How to fix

For the first reason, simply provide a doGet or doPost trigger (or both) function. For the second, make sure that all routes of your app end with creation of TextOutput or HtmlOutput:

//doGet returning HTML
function doGet(e) {
  return HtmlService.createHtmlOutput("<p>Some text</p>");
}

//doPost returning text
function doPost(e) {
  const { parameters } = e;
  const echoed = JSON.stringify(parameters);
  return ContentService.createTextOutput(echoed);
}

Note that there should be only one trigger function declared - treat them as entry points to your application.

If the trigger relies on parameter / parameters to route responses, make sure that the request URL is structured as "baseURL/exec?query" or "baseURL/dev?query" where query contains parameters to pass.

Related Q&As

  1. Redeploying after declaring triggers


Message

We're sorry, a server error occurred. Please wait a bit and try again.

Description

This one is the most cryptic error and can occur at any point with nearly any service (although DriveApp usage is particularly susceptible to it). The error usually indicates a problem on Google's side that either goes away in a couple of hours/days or gets fixed in the process.

How to fix

There is no silver bullet for that one and usually, there is nothing you can do apart from filing an issue on the issue tracker or contacting support if you have a GSuite account. Before doing that one can try the following common remedies:

  1. For bound scripts - creating a new document and copying over the existing project and data.
  2. Switch to using an advanced Drive service (always remember to enable it first).
  3. There might be a problem with a regular expression if the error points to a line with one.

Don't bash your head against this error - try locating affected code, file or star an issue and move on


Syntax error without apparent issues

This error is likely to be caused by using an ES6 syntax (for example, arrow functions) while using the deprecated V8 runtime (at the time of writing the GAS platform uses V8).

How to fix

Open "appscript.json" manifest file and check if runtimeVersion is set to "V8", change it if not, or remove any ES6 features otherwise.


Quota-related errors

There are several errors related to quotas imposed on service usage. Google has a comprehensive list of those, but as a general rule of thumb, if a message matches "too many" pattern, you are likely to have exceeded the respective quota.

Most likely errors encountered:

  • Service invoked too many times: service name
  • There are too many scripts running
  • Service using too much computer time for one day
  • This script has too many triggers

How to fix

In most cases, the only fix is to wait until the quota is refreshed or switch to another account (unless the script is deployed as a Web App with permission to "run as me", in which case owner's quotas will be shared across all users).

To quote documentation at the time:

Daily quotas are refreshed at the end of a 24-hour window; the exact time of this refresh, however, varies between users.

Note that some services such as MailApp have methods like getRemainingDailyQuota that can check the remaining quota.

In the case of exceeding the maximum number of triggers one can check how many are installed via getProjectTriggers() (or check "My triggers" tab) and act accordingly to reduce the number (for example, by using deleteTrigger(trigger) to get rid of some).

Related canonical Q&As

  1. How are daily limitations being applied and refreshed?
  2. "Maximum execution time exceeded" problem
  3. Optimizing service calls to reduce execution time


References

  1. How to make error messages more meaningful
  2. Debugging custom functions

这篇关于如何解决 Google Apps Script 开发中的常见错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆