Haeminway haeminway
한국어
Back to Tech Notes
1 min read

The 6-Minute GAS Limit: Chunk and Resume Before It Kills You

Apps Script kills any single run at 6 minutes. Design bulk jobs as chunk + cursor + time-trigger resume, or you get the half-processed-data accident.

A single execution is force-killed at 6 minutes. Try to process 10,000 rows in one loop, hit the limit, and the script dies mid-run: leaving you half-processed. Run it again and it re-touches rows it already handled.

Why it matters

Half-applied data is worse than untouched data. You don’t know how far it got, so you need manual recovery: and that time eats the time automation was supposed to save. Trigger runtime also has its own daily cap (roughly 90 min/day on a consumer account).

The fix: chunk + cursor + trigger resume

Don’t try to finish in one shot. Save your position (a cursor) and use a time trigger to resume the next chunk.

const JOB_KEY = "sync.cursor";

function runSyncJob() {
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(10000)) return; // prevent overlap

  try {
    const props = PropertiesService.getScriptProperties();
    const cursor = Number(props.getProperty(JOB_KEY) || 0);
    const result = processChunk(cursor, 300); // 300 rows at a time

    if (result.done) {
      props.deleteProperty(JOB_KEY);
      deleteTriggers_("runSyncJob"); // clean up when finished
      return;
    }
    props.setProperty(JOB_KEY, String(result.nextCursor));
    scheduleOnce_("runSyncJob", 60 * 1000); // resume in 1 min
  } finally {
    lock.releaseLock();
  }
}

function scheduleOnce_(handler, delayMs) {
  deleteTriggers_(handler);
  ScriptApp.newTrigger(handler).timeBased().after(delayMs).create();
}
function deleteTriggers_(handler) {
  ScriptApp.getProjectTriggers()
    .filter((tr) => tr.getHandlerFunction() === handler)
    .forEach((tr) => ScriptApp.deleteTrigger(tr));
}

Easy to miss

  • Always delete triggers. You get 20 triggers per script. If you don’t delete the existing same-handler trigger before creating a one-shot, you hit the cap fast.
  • Prevent overlap. Use LockService so a trigger that overlaps the previous run doesn’t process the same cursor twice.
  • Clean up on finish. Delete the cursor and trigger when done, or an empty job keeps firing every minute.

Deeper: size chunks by measuring

Start chunk size at 100–500 rows and tune against real run time. If one chunk passes half of the 6 minutes, shrink it. One line to keep: split into units that finish under 6 minutes, save your place, and resume.

Frequently asked questions

Why does my Google Apps Script stop at 6 minutes?
Apps Script force-kills any single execution at about 6 minutes (consumer accounts). Process a bulk job in one loop and you hit the limit mid-run, leaving the data half-applied.
How do I get around the 6-minute execution limit?
Don't finish in one run. Save your position as a cursor, then resume the next chunk with a time-based trigger (chunk + cursor + trigger resume). Use LockService to prevent overlapping runs.
Do triggers remove the time limit?
No. Total trigger runtime is also capped (about 90 minutes/day on consumer accounts). Size your chunks and run frequency to stay within that budget.