GEMINI LABJP
PRO35 — July 17, the date reports had pointed to, has passed without an official Gemini 3.5 Pro announcement or model card. July 24 is being cited as the fallbackNB2LITE — Nano Banana 2 Lite, otherwise known as Gemini 3.1 Flash-Lite Image, arrives as the fastest of the family: roughly four seconds per image at $0.034 per thousandOMNI — Gemini Omni Flash enters public preview, generating video up to ten seconds long at $0.10 per second of outputEDIT — Omni Flash is built around conversational editing. Swap a character, relight a scene, or change the angle in plain language, and the original audio and video tracks stay intactSYNTHID — Both new models carry SynthID watermarking, so anything they produce can be checked for provenance from inside the Gemini appSHUTDOWN — The older image generation models are deprecated and switch off on August 17. Worth checking your migration windowPRO35 — July 17, the date reports had pointed to, has passed without an official Gemini 3.5 Pro announcement or model card. July 24 is being cited as the fallbackNB2LITE — Nano Banana 2 Lite, otherwise known as Gemini 3.1 Flash-Lite Image, arrives as the fastest of the family: roughly four seconds per image at $0.034 per thousandOMNI — Gemini Omni Flash enters public preview, generating video up to ten seconds long at $0.10 per second of outputEDIT — Omni Flash is built around conversational editing. Swap a character, relight a scene, or change the angle in plain language, and the original audio and video tracks stay intactSYNTHID — Both new models carry SynthID watermarking, so anything they produce can be checked for provenance from inside the Gemini appSHUTDOWN — The older image generation models are deprecated and switch off on August 17. Worth checking your migration window
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 Script7Gemini API190LockServicePropertiesServiceGoogle Workspace15

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 $10 for lifetime access
View Membership →

Related Articles

Workspace2026-07-13
Retiring the Poll That Waits on an Overnight Batch — An Apps Script doPost Sink for Gemini Signals
Polling a Gemini batch or long-running operation every five minutes from an Apps Script time trigger quietly stacks up UrlFetch calls and latency. Receive the webhook in doPost, treat it as an unverified signal, then confirm authoritatively and apply idempotently.
Workspace2026-07-08
Your Apps Script Gemini Automation Fails Every Month-End — Budgeting Against the UrlFetch Daily Quota
Apps Script automations that call Gemini stall on the UrlFetch daily call quota — a separate ceiling from the 6-minute limit and trigger counts. Here is a daily budget governor with backlog carry-over that keeps the job running on busy days, with working code and a verified simulation.
Workspace2026-06-29
Keeping Apps Script + Gemini Automations on Least Privilege: Explicit Scopes and Catching Scope Creep
Apps Script automations that call Gemini quietly accumulate OAuth scopes. Here is how to declare explicit scopes in appsscript.json, catch scope creep in CI, and avoid forcing every user to re-consent.
📚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
See all →