DEV Community

Cover image for Google Apps Script Debugging: Why Your Script Works Until It Doesn't
Mahrosh is Here
Mahrosh is Here

Posted on

Google Apps Script Debugging: Why Your Script Works Until It Doesn't

Google Apps Script Debugging: Why Your Script Works Until It Doesn't

Apps Script errors are rarely as mysterious as they look.

Usually, the error message is telling you something useful. The difficult part is connecting that message to what your script was actually doing when it failed.

A function might work perfectly with one spreadsheet and fail with another.

A trigger might work when you run a function manually but fail automatically.

An API call might work ten times and then suddenly hit a quota or return something your code wasn't expecting.

Here are some of the Apps Script problems I encounter most often and how to approach them.

1. Exception: Cannot call method...

One of the most frustrating errors is when Apps Script tells you that it cannot call a method on something.

For example:

const sheet = SpreadsheetApp.getActiveSpreadsheet()
  .getSheetByName("Tasks");

const data = sheet.getDataRange().getValues();
Enter fullscreen mode Exit fullscreen mode

If getSheetByName("Tasks") returns null, the next line will fail.

The problem isn't getDataRange().

The problem happened one line earlier.

A simple defensive check can make this much easier to diagnose:

const sheet = SpreadsheetApp.getActiveSpreadsheet()
  .getSheetByName("Tasks");

if (!sheet) {
  throw new Error("Sheet 'Tasks' was not found.");
}
Enter fullscreen mode Exit fullscreen mode

When you see a "cannot call method" error, work backward from the failing line and ask:

Which object could actually be undefined or null?

2. Cannot find sheet

Sheet names are strings, which means tiny differences matter.

These are different:

Tasks
tasks
Tasks 
Task
Enter fullscreen mode Exit fullscreen mode

A trailing space can be enough to break your script.

Instead of assuming the sheet exists, check it:

const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName("Tasks");

if (!sheet) {
  throw new Error("Expected sheet was not found.");
}
Enter fullscreen mode Exit fullscreen mode

For debugging larger projects, you can also inspect the available sheet names:

const sheets = spreadsheet.getSheets();

sheets.forEach(sheet => {
  console.log(sheet.getName());
});
Enter fullscreen mode Exit fullscreen mode

This can immediately reveal spelling or naming problems.

3. Authorization errors

Apps Script interacts with Google services on your behalf.

That means operations involving Gmail, Drive, Calendar, Sheets, and other services may require authorization.

A script can therefore fail even when the JavaScript itself is correct.

If you've recently added a new service or changed what your script accesses, check whether the required authorization has been granted.

Also remember that authorization behavior can differ depending on whether you're running a function manually, through a trigger, or under a different account.

4. Trigger-specific failures

This is one of the biggest sources of confusion.

You run:

function processTasks() {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

It works.

Then you create a trigger for processTasks().

Suddenly it fails.

Why?

Because manually running a function and running it through a trigger aren't necessarily the same execution context.

Some functions also rely on information that isn't available during a trigger execution.

For example, code that depends on an active spreadsheet, active user, selected cell, or UI can behave differently when nobody is actively sitting in the editor.

When debugging a trigger, ask:

  1. What type of trigger is running?
  2. What account executes it?
  3. What services does it access?
  4. Does it depend on an active user or UI?
  5. What does the execution log show?

5. Quota errors

Apps Script isn't an unlimited execution environment.

Depending on the service and account, there are limits around execution time, service calls, emails, API requests, and other operations.

A common performance problem looks like this:

rows.forEach(row => {
  sheet.getRange(row[0], 2).setValue("Processed");
});
Enter fullscreen mode Exit fullscreen mode

It may work for 20 rows.

With thousands of rows, repeatedly communicating with the spreadsheet can become expensive.

Instead, collect your changes and write them in batches where possible.

The general principle is:

Reduce the number of service calls.

6. undefined values

JavaScript problems often become Apps Script problems.

Consider:

const name = row[5];
console.log(name.toUpperCase());
Enter fullscreen mode Exit fullscreen mode

What happens if row[5] doesn't contain a value?

You'll get an error because you're trying to call toUpperCase() on something that isn't a string.

During debugging, don't assume your data has the shape you expect.

Inspect it:

console.log(JSON.stringify(row));
Enter fullscreen mode Exit fullscreen mode

Then verify the value before using it.

if (typeof name === "string") {
  console.log(name.toUpperCase());
}
Enter fullscreen mode Exit fullscreen mode

7. API response errors

External APIs introduce another layer of uncertainty.

Your code may expect:

{
  "status": "success",
  "data": []
}
Enter fullscreen mode Exit fullscreen mode

But the API might return:

{
  "error": "Unauthorized"
}
Enter fullscreen mode Exit fullscreen mode

If your code immediately does:

const data = response.data;
Enter fullscreen mode Exit fullscreen mode

you may end up debugging the wrong line.

Always inspect the response status and body before assuming the expected structure exists.

const response = UrlFetchApp.fetch(url, {
  muteHttpExceptions: true
});

console.log(response.getResponseCode());
console.log(response.getContentText());
Enter fullscreen mode Exit fullscreen mode

muteHttpExceptions can be particularly useful while diagnosing HTTP failures because it lets your script inspect the response instead of immediately throwing an exception.

8. Timeouts

Apps Script executions have time limits.

A script that works with 100 rows may become unreliable when the spreadsheet grows to 50,000 rows.

Look for expensive operations such as:

  • repeated calls to Sheets
  • unnecessary API requests
  • processing the same data multiple times
  • large loops
  • unnecessary reads and writes

One of the first optimization questions should be:

How much work is the script actually doing?

9. Logging

Good logging can turn debugging from guessing into investigation.

Instead of:

processData();
Enter fullscreen mode Exit fullscreen mode

you can temporarily add:

console.log("Starting processData");
console.log(`Rows found: ${rows.length}`);
Enter fullscreen mode Exit fullscreen mode

For important values:

console.log(JSON.stringify(data));
Enter fullscreen mode Exit fullscreen mode

The goal isn't to log everything forever.

It's to capture enough information to understand the execution path.

When debugging a complicated automation, useful logs can answer:

  • Did the function start?
  • Which branch executed?
  • How many records were found?
  • What value caused the failure?
  • Did the API respond?
  • Did the script reach the final step?

10. Reproduce the error

This is probably the most important debugging habit.

Don't immediately change five things at once.

First reproduce the problem.

Then reduce it.

If a function processes 5,000 rows, try 10.

If it uses three APIs, test each request separately.

If a trigger fails, run the underlying logic independently where possible.

The smaller the reproduction case, the easier it becomes to identify the actual cause.

Where AI Debugging Helps

AI is particularly useful when you give it enough context.

A vague prompt such as:

My Apps Script doesn't work. Fix it.

doesn't provide much to work with.

A better debugging prompt includes:

  • the exact error message
  • the function that failed
  • the relevant file or code section
  • what you expected to happen
  • what actually happened
  • any recent change that may have caused the problem

For example:

This trigger fails with Cannot read properties of null. Here is the function, the sheet structure, and the execution log. The expected behavior is to update the Status column after a form submission.

That's a much more useful debugging problem.

AI can help explain the error, identify suspicious lines, suggest defensive checks, and propose possible fixes.

But you still need to verify the suggested change.

Fixing Errors Inside the Apps Script Workflow

This is also where editor-integrated AI assistance can be useful.

Google Apps Script Copilot includes a Fix with GS Copilot workflow that can use an execution error in context to help identify and apply a fix.

I see that as more useful for debugging than simply asking an AI to generate an entire script from scratch.

The developer still needs to understand the change, review it, and test the result.

A Better Debugging Process

When an Apps Script suddenly stops working, I generally work through this sequence:

Read the error
      ↓
Find the failing line
      ↓
Inspect the values
      ↓
Reproduce the problem
      ↓
Reduce the test case
      ↓
Identify the root cause
      ↓
Apply one change
      ↓
Run the test again
Enter fullscreen mode Exit fullscreen mode

The important part is not fixing the error as quickly as possible.

It's understanding why it happened.

Once you know that, you're much less likely to create the same bug somewhere else.

Top comments (0)