The 2 AM Production Crash
You just shipped a Google Sheets tool for a client, and it worked perfectly during your solo tests. But as soon as twenty people open the file at once, or someone pastes a 5,000-row dataset, the script hits a wall. The execution log fills with a single, frustrating message:
Exception: Too many simultaneous invocations: Spreadsheet
Google imposes a hard cap on how many script instances can run at the exact same time. For most Google Workspace accounts, this limit sits around 30 concurrent executions. If you exceed this—often by using custom functions or high-frequency triggers—Google kills the process to save its own server resources.
TL;DR: How to Stop the Crashing
- Implement LockService: Force scripts to wait in a queue rather than running all at once.
- Batch Custom Functions: Don't call a function 100 times for 100 rows. Pass the entire range
(A2:A101)and process it in one go. - Use CacheService: Save results for 10 minutes so identical calculations don't re-run the script.
Why This Error Happens
Imagine you have a custom function like =GET_VAT(A1). If you drag that formula down 500 rows, Google Sheets doesn't run them one by one. It tries to calculate all 500 simultaneously. Since 500 is much larger than the ~30 allowed slots, the spreadsheet panics and throws the exception.
Approach 1: Queuing with LockService
Think of LockService as a "Take a Number" machine at a deli. It prevents multiple executions from trying to write to the same sheet at the same moment. It effectively turns a chaotic pile-up into a tidy line.
function safeWriteToSheet(data) {
const lock = LockService.getScriptLock();
try {
// Request a lock and wait up to 30 seconds for others to finish
lock.waitLock(30000);
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Logs');
sheet.appendRow([new Date(), data]);
// Flush ensures changes are written before the lock is released
SpreadsheetApp.flush();
} catch (e) {
console.error('Lock timeout: ' + e.message);
throw new Error('The server is busy. Please wait a few seconds and try again.');
} finally {
// Always release the lock, even if the code fails
lock.releaseLock();
}
}
Approach 2: Range Batching (The Best Fix)
If your error stems from custom formulas, LockService might actually make your sheet feel sluggish because every cell has to wait its turn. The real solution is to rewrite your function to handle an entire range (an Array) instead of a single value.
The Inefficient Way:
Formula used in 1,000 cells: =USD_TO_EUR(A2)
// AVOID: This triggers 1,000 separate script executions
function USD_TO_EUR(value) {
return value * 0.92;
}
The Pro Way:
Formula used in exactly 1 cell: =USD_TO_EUR(A2:A1001)
// RECOMMENDED: This triggers exactly 1 execution to process 1,000 rows
function USD_TO_EUR(input) {
if (Array.isArray(input)) {
// Process the 2D array returned by the range
return input.map(row => row.map(cell => cell * 0.92));
}
return input * 0.92;
}
By passing the entire range, you slash your invocation count from 1,000 down to 1. This is the most effective way to stay under Google's concurrency limits.
Approach 3: Caching Results
If your script fetches data from an external API (like a currency converter or weather service), don't fetch the same data twice. Use CacheService to store results for a few minutes. This prevents new users from triggering a fresh script execution for data you already have.
function getCachedRate(currencyPair) {
const cache = CacheService.getScriptCache();
const cachedValue = cache.get(currencyPair);
if (cachedValue) return cachedValue;
// If not in cache, perform the expensive API call
const rate = fetchRateFromAPI(currencyPair);
// Store in cache for 15 minutes (900 seconds)
cache.put(currencyPair, rate, 900);
return rate;
}
Verification: Stress Testing the Fix
Don't assume it's fixed just because it works for you. Try these three tests:
- The Undo Test: Highlight 200 cells containing your custom function, delete them, and hit
Cmd+Z(orCtrl+Z). This forces a massive simultaneous recalculation. - The Execution Log: Open the Apps Script editor and click Executions. Look for overlapping start times. If they all show "Completed" instead of "Failed," your
waitLockis working. - The Multi-User Jam: Ask two colleagues to spam the "Run" button or edit the same range at the same time. If the script queues correctly without crashing, you're ready for production.
Summary
The "Too many simultaneous invocations" error isn't a bug in your math—it's a traffic jam. Use LockService to manage the queue and range-based batching to reduce the number of cars on the road. These two changes alone will solve 95% of concurrency issues in Google Apps Script.

