Skip to content
The 5 Things AI Gets Wrong About Google Apps Script Every Time

The 5 Things AI Gets Wrong About Google Apps Script Every Time

20 November 2025
#Automation#AI#Google Apps Script#ChatGPT

Why This Matters

More businesses are using ChatGPT, Gemini, and Copilot to write Google Apps Script. And it works; sort of. The code runs in testing. It handles the basic case. The syntax is correct.

But certain patterns appear in AI-generated Apps Script over and over again, and they break in the same ways. I see these in client code regularly; scripts that “mostly work” until they don’t, always for one of these five reasons.


1. Using SpreadsheetApp.getActiveSpreadsheet() in Time Triggers

What AI writes:

function processData() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Data");
  // ... process data
}

Why it fails: getActiveSpreadsheet() returns the spreadsheet the user currently has open. It works perfectly when you run the script manually from the script editor or from a custom menu; because there is an active spreadsheet.

But time-based triggers (the ones that run on a schedule; every hour, every morning at 6am) don’t have an active spreadsheet. They run in the background with no user context. getActiveSpreadsheet() returns null, and the script fails silently or throws a cryptic error.

What production code uses:

function processData() {
  var ss = SpreadsheetApp.openById("YOUR_SPREADSHEET_ID");
  var sheet = ss.getSheetByName("Data");
  // ... process data
}

Always reference spreadsheets by ID for any script that runs on a trigger. The AI almost never does this by default because most training examples use the interactive context.


2. Reading and Writing Cells One at a Time

What AI writes:

function updatePrices() {
  var sheet = SpreadsheetApp.openById("...").getSheetByName("Products");
  var lastRow = sheet.getLastRow();
  
  for (var i = 2; i <= lastRow; i++) {
    var price = sheet.getRange(i, 3).getValue();
    var newPrice = price * 1.1;
    sheet.getRange(i, 4).setValue(newPrice);
  }
}

Why it fails: Each getValue() and setValue() call is a separate round-trip to Google’s servers. With 10 rows, you won’t notice. With 1,000 rows, it takes minutes. With 10,000 rows, it times out (Apps Script has a 6-minute execution limit for most triggers).

This is the single most common performance problem in Apps Script, and AI generates it almost every time.

What production code uses:

function updatePrices() {
  var sheet = SpreadsheetApp.openById("...").getSheetByName("Products");
  var data = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues();
  
  var results = data.map(function(row) {
    return [row[2] * 1.1]; // Column D = Column C * 1.1
  });
  
  sheet.getRange(2, 4, results.length, 1).setValues(results);
}

One read. One write. Runs in seconds regardless of row count. Batch operations are the first thing any experienced Apps Script developer learns, and the last thing AI thinks to use.


3. Ignoring the 6-Minute Execution Limit

What AI writes: Long-running scripts that process data sequentially with no awareness of time limits.

Why it fails: Google Apps Script enforces a maximum execution time of 6 minutes for simple triggers and most time-based triggers (30 minutes for some Workspace account executions). AI-generated scripts routinely exceed this when processing large datasets or making multiple API calls.

The script doesn’t just slow down; it terminates. Any work done after the last successful checkpoint is lost. If the script was halfway through updating 5,000 rows, you end up with 2,500 updated and 2,500 not, with no record of where it stopped.

What production code uses:

function processBatch() {
  var properties = PropertiesService.getScriptProperties();
  var startRow = parseInt(properties.getProperty("lastProcessedRow") || "2");
  var sheet = SpreadsheetApp.openById("...").getSheetByName("Data");
  var lastRow = sheet.getLastRow();
  var startTime = new Date().getTime();
  
  for (var i = startRow; i <= lastRow; i++) {
    // Check if we're approaching the time limit (5 minutes of 6)
    if (new Date().getTime() - startTime > 300000) {
      properties.setProperty("lastProcessedRow", i.toString());
      // Trigger will pick up from here on next run
      return;
    }
    // ... process row
  }
  
  // All done - reset for next full run
  properties.deleteProperty("lastProcessedRow");
}

The script tracks its progress, watches the clock, and saves its position when time is running low. The next trigger run picks up exactly where it left off. This pattern is standard in production Apps Script, and virtually absent from AI-generated code.


4. Not Handling Google API Errors

What AI writes:

function sendEmails() {
  var sheet = SpreadsheetApp.openById("...").getSheetByName("Contacts");
  var data = sheet.getDataRange().getValues();
  
  for (var i = 1; i < data.length; i++) {
    GmailApp.sendEmail(data[i][1], "Subject", "Body");
  }
}

Why it fails: Google’s APIs fail. Not often, but reliably. Temporary server errors, rate limiting, quota exhaustion, invalid email addresses, network glitches. AI-generated code almost never includes error handling, which means one bad email address in row 47 stops the entire script, and rows 48-500 never get their emails.

What production code uses:

function sendEmails() {
  var sheet = SpreadsheetApp.openById("...").getSheetByName("Contacts");
  var data = sheet.getDataRange().getValues();
  var errors = [];
  
  for (var i = 1; i < data.length; i++) {
    try {
      GmailApp.sendEmail(data[i][1], "Subject", "Body");
      sheet.getRange(i + 1, 5).setValue("Sent");
      Utilities.sleep(100); // Rate limiting buffer
    } catch (e) {
      errors.push("Row " + (i + 1) + ": " + e.message);
      sheet.getRange(i + 1, 5).setValue("Error: " + e.message);
    }
  }
  
  if (errors.length > 0) {
    GmailApp.sendEmail("admin@example.com", "Email batch errors", errors.join("\n"));
  }
}

Each email is wrapped in try/catch. Failures are logged, not fatal. The script continues processing. A summary of errors is sent to the admin. And a small delay between sends prevents hitting rate limits.


5. Creating Triggers Inside the Script Without Cleanup

What AI writes:

function setup() {
  ScriptApp.newTrigger("processData")
    .timeBased()
    .everyHours(1)
    .create();
}

Why it fails: Every time setup() runs, it creates a new trigger. Run it twice and you have two triggers; your script fires twice per hour. Run it five times during debugging and you have five triggers. There’s no built-in deduplication.

I’ve seen client accounts with 15-20 duplicate triggers for the same function, all created by an AI-generated setup script that was run multiple times during testing. The script was sending 15 copies of the same daily report.

What production code uses:

function setup() {
  // Delete any existing triggers for this function first
  var triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(function(trigger) {
    if (trigger.getHandlerFunction() === "processData") {
      ScriptApp.deleteTrigger(trigger);
    }
  });
  
  // Now create a fresh trigger
  ScriptApp.newTrigger("processData")
    .timeBased()
    .everyHours(1)
    .create();
}

Always clean up existing triggers before creating new ones. It’s a simple pattern that AI consistently overlooks.


The Deeper Problem

These five patterns aren’t random mistakes. They reveal a fundamental limitation: AI writes code that works in testing but breaks in production. It optimises for correctness in a single, clean execution, not for the messy reality of daily automated runs against real data at real volume.

Production Apps Script development is less about writing code and more about knowing what breaks. Quotas, execution limits, API rate limiting, trigger lifecycle management, concurrent execution conflicts, timezone handling, shared drive permissions; these are the things that separate a script from a system.

AI gives you the script. Experience gives you the system.


Got AI-Generated Code That’s Misbehaving?

At Empower Automation, we regularly audit and fix Apps Script that was written by AI or non-specialists. Sometimes it’s a quick fix; adding batch operations and error handling. Sometimes it needs restructuring. Either way, we can tell you exactly what’s likely to break and what it takes to make it production-ready.

Book a free 15-minute automation audit →

Send us your script. We’ll tell you which of these five patterns it’s hitting, and anything else we spot.


Nicola Berry is the founder of Empower Automation, based in Falkirk, Scotland. Fixing the scripts AI writes and building the ones it can’t.

Google Workspace

Professional Email & Tools for Your Business

Get you@yourcompany.com, plus video meetings, secure cloud storage, and the powerful admin controls you need to scale. Same tools I use to build your automations.

Custom business email
Secure file sharing & collaboration
Security and management controls