◉GEMINI LABJP
●3.8 FLASH — 3.7 Flash was deprecated on Oct 8. Requests to the old model are routed to 3.8 Flash automatically●NANO 2.1 — gemini-nano-banana-2.1 is GA. gemini-3.1-flash-image is deprecated with no shutdown date yet●DEADLINE — veo-3.1 previews and gemini-omni-flash-preview shut down Oct 22; the Deep Research agent follows Oct 23 (12-13 days left)●Q&A — Users report Gemini CLI marks a subagent run as a success even after it stops at MAX_TURNS●TTS/LIVE — 2.5-era audio and TTS previews shut down Nov 17, 38 days left●NEW — Deprecated with no shutdown date? Re-pick your image-model target with three conditions●3.8 FLASH — 3.7 Flash was deprecated on Oct 8. Requests to the old model are routed to 3.8 Flash automatically●NANO 2.1 — gemini-nano-banana-2.1 is GA. gemini-3.1-flash-image is deprecated with no shutdown date yet●DEADLINE — veo-3.1 previews and gemini-omni-flash-preview shut down Oct 22; the Deep Research agent follows Oct 23 (12-13 days left)●Q&A — Users report Gemini CLI marks a subagent run as a success even after it stops at MAX_TURNS●TTS/LIVE — 2.5-era audio and TTS previews shut down Nov 17, 38 days left●NEW — Deprecated with no shutdown date? Re-pick your image-model target with three conditions
Articles/Workspace
◧ Workspace/2026-07-01Advanced

When Two Triggers Write at Once, Your Gemini Result Quietly Vanishes — A Durable Result Store for Apps Script

Storing Gemini results from several Apps Script triggers loses writes through read-modify-write races and PropertiesService size limits. Build a result store that survives, using LockService, a durable sink, and idempotency keys.

Apps Script12Gemini API247LockServicePropertiesServiceGoogle Workspace21

✦ Premium Article

One morning I was reviewing an Apps Script job that wrote Gemini's overnight summaries back into a spreadsheet, and one of three jobs that had clearly run was simply missing from the state store. No error in the logs. The execution history showed two time-driven triggers firing within the same second. If you run several automations in parallel as an indie developer, this is the kind of trap you eventually step on.

The cause was neither Gemini nor an Apps Script bug. It was the ordinary "read, add, write back" update I had written myself. Here I'll reproduce why that update quietly breaks, then build a result store that survives concurrent triggers using LockService, a durable sink, and an idempotency key.

Why it disappears: the read-modify-write race

Start with the common pattern of keeping all results in one JSON blob.

function appendResultNaive(jobId, text) {
  const props = PropertiesService.getScriptProperties();
  const raw = props.getProperty('results') || '{}';
  const map = JSON.parse(raw);   // (A) read
  map[jobId] = text;             // (B) add
  props.setProperty('results', JSON.stringify(map)); // (C) write back
}

As long as only one trigger runs, this is fine. The trouble starts when two triggers reach (A) at nearly the same time. Both read the same map that does not yet contain jobId, each adds its own result, and each writes back at (C). Whichever execution reaches (C) last overwrites the result written first. That is a classic lost update, and the losing job vanishes without raising anything.

Apps Script is single-threaded within one execution, but triggers and concurrent runs execute as separate processes that can overlap. PropertiesService does nothing to protect the gap between your read and your write, and that gap is where the race lives.

Look the PropertiesService limits in the eye

Before fixing the race, settle what belongs in PropertiesService at all. The property store has firm ceilings, and crossing them makes setProperty throw.

StorePer-value limitTotal limitBest used for
PropertiesService~9 KB~500 KBSmall coordination state, flags, an index
CacheService~100 KB(volatile, up to 6 hours)A hot cache to speed up reads
Drive / SpreadsheetEffectively largeLargeA durable sink for the payload itself

A Gemini result is several KB even for a summary, and easily over 9 KB once it includes body text. So "accumulate the result body directly in PropertiesService" breaks on size before it ever races. If you want to measure a value before storing it:

function fitsInProperty_(text) {
  const bytes = Utilities.newBlob(text).getBytes().length;
  return bytes < 9 * 1024;  // under 9KB fits in one value
}

That leads to one principle: keep only a small index — which job, where, and when — in PropertiesService, and push the result body to a separate durable sink.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Reproduce how PropertiesService's 9KB-per-value and 500KB-total limits plus read-modify-write races silently overwrite a Gemini result
✦Size LockService.getScriptLock with a sensible waitLock budget and pick the right lock granularity, shown with the failure cases
✦Ship a complete Apps Script store that offloads large output to Drive and blocks double-apply with an idempotency key
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

◧ Workspace2026-10-03
Estimating a Sheets-Driven Gemini Bill Across the Year-End Price Switch with Apps Script
If you call the Gemini API from a spreadsheet, log the token counts per call and estimate the month in two columns: the intro price that ends on December 31, 2026 and the standard price from January 1, 2027. Apps Script, with real output.
◧ Workspace2026-09-01
When a New Gemini Feature Hasn't Reached Your Workspace, Here's How to Tell Waiting From Broken
A step-by-step way to decide whether a missing Gemini feature in Google Workspace is still rolling out or actually misconfigured, using release tracks and rollout pace, plus a small Apps Script ledger that does the counting for you.
◧ Workspace2026-09-22
Workspace turned third-party connectors on by default. Two screens to check before you decide
Since September 15, 2026, Gemini in Workspace connects to Asana, Salesforce and five other tools over MCP, and the setting ships on by default. Here is how I count what is actually enabled, from both the Admin console and the user side panel.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links