●CHAT — Ask Gemini in Chat goes live today, August 26, turning Google Chat into a single command line for search, drafting, catching up, and task management●LIMITS — Ask Gemini in Chat comes with promotional higher limits through October 1, after which standard usage limits apply●SHEETS — Sheets canvas turns a spreadsheet into an interactive, read-write application from a plain-language prompt●MEET — You can now start a Gemini note-taking session straight from the Google Meet home screen, including for in-person meetings, with the summary, action items, and full transcript saved to a Google Doc●MODELS — Gemini 3.7 Flash reached general availability on August 13, with introductory pricing running through December 31, 2026●DEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, five days from now. The ER 2 endpoints have been in public preview since July 30●CHAT — Ask Gemini in Chat goes live today, August 26, turning Google Chat into a single command line for search, drafting, catching up, and task management●LIMITS — Ask Gemini in Chat comes with promotional higher limits through October 1, after which standard usage limits apply●SHEETS — Sheets canvas turns a spreadsheet into an interactive, read-write application from a plain-language prompt●MEET — You can now start a Gemini note-taking session straight from the Google Meet home screen, including for in-person meetings, with the summary, action items, and full transcript saved to a Google Doc●MODELS — Gemini 3.7 Flash reached general availability on August 13, with introductory pricing running through December 31, 2026●DEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, five days from now. The ER 2 endpoints have been in public preview since July 30
The Model Didn't Ship on Its Rumored Date — Read Your Context Limit From the API, Not the Headlines
July 17 came and went with no official word on Gemini 3.5 Pro. Instead of baking rumored numbers into constants, here's a context budget layer that reads the real limit from models.get and degrades quietly when input overflows.
This morning, the date I'd seen in headline after headline was today. My notes said, simply: "7/17, 3.5 Pro." I opened the docs, pulled the model list, checked the pricing page. No gemini-3.5-pro anywhere. As of this writing, no announcement from Google either.
A few days ago I wrote about measuring my prompts' token counts while waiting for the release. At the time I wrote "shipping July 17." Today that premise is wrong. Premises being wrong isn't unusual. What matters is where in the code you put the premise.
Had I written MAX_CONTEXT = 2_000_000 as a constant and pre-seeded gemini-3.5-pro into my config, production would have broken today. A rumored number is fine in an article as long as you say "reportedly." The moment it lands in config, it becomes a promise.
This post pushes rumored values out of configuration, reads the limit from the model itself, and degrades instead of crashing when input overflows. The code runs as-is — drop the layer in front of your calls and the next slipped date won't cost you anything.
Separate what you can verify from what you can't
Start with only the facts you can check from your own terminal today. Leave this boundary fuzzy and rumor leaks back into the design somewhere.
Item
Status
How to verify
gemini-3.5-pro model card
Not present in the public API docs
Check the model list in the docs
2M-token context
Reported, unconfirmed
No way to verify right now
Pricing
Reported, unconfirmed
Not listed on the pricing page
Latest generally available model
gemini-3.5-flash
models.list
What gemini-flash-latest resolves to
Now gemini-3.5-flash
models.get
Look down the right-hand column and the line draws itself. Some rows are verified by hitting an API; others rest on someone saying so. Building config on the first kind only — that's the whole idea here.
What pre-seeding an unreleased model ID actually does
Putting the model ID in an environment variable ahead of time feels like preparation. I've done it. The result was unsurprising: a NOT_FOUND, and a failed startup health check.
// Before: reporting-derived values baked into configconst MODEL = process.env.GEMINI_MODEL ?? "gemini-3.5-pro"; // doesn't exist yetconst MAX_CONTEXT = 2_000_000; // unconfirmed numberasync function ask(prompt) { // If MODEL doesn't exist, you only find out here return ai.models.generateContent({ model: MODEL, contents: prompt });}
The nasty part is that the failure is deferred all the way to call time. The deploy passes. Startup passes. The first user request breaks. Worse, MAX_CONTEXT is never validated by anything, yet it goes on driving truncation decisions forever — an unconfirmed limit for a nonexistent model, shaping input for a model that does exist.
I covered pinning versus depending on the default in detecting silent default-model swaps. What that post didn't cover is this direction: writing down an ID that isn't there yet.
✦
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
✦A working JavaScript budget layer that treats models.get inputTokenLimit as the single source of truth and pushes reporting-derived numbers out of your config
✦A countTokens-with-headroom procedure, plus a fallback chain that splits, summarizes, or fails loudly instead of silently truncating your input
✦A ~20-line CI gate that catches unreleased model IDs before they reach production, and why the failure belongs in a pull request rather than a user's first request
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.
Gemini's model metadata already carries the input token limit. One models.get call retrieves it. There's no reason for a constant.
import { GoogleGenAI } from "@google/genai";const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });// Fetch once at startup, hold it in-processasync function loadBudget(model) { const info = await ai.models.get({ model }); return { model: info.name, // the resolved concrete model inputLimit: info.inputTokenLimit, // the published limit outputLimit: info.outputTokenLimit, };}const budget = await loadBudget("gemini-flash-latest");console.log(budget);// => { model: 'models/gemini-3.5-flash', inputLimit: ..., outputLimit: ... }
Pass an alias like gemini-flash-latest and info.name comes back with the resolved concrete model. You learn what the alias points at from a return value rather than a guess. Log it, and later you can trace exactly when the target swapped underneath you.
Note that this layer budgets length and cost — not capability. Whether thinking levels or structured output are supported is a separate probe, which I kept in a startup capability detection layer. Cram both into one layer and a failure in one takes the other down with it. Keeping the axes side by side, but separate, has been easier to live with.
Measure input with countTokens, then leave headroom
With the limit in hand, check whether the input fits before you send it. countTokens is an unbilled estimate meant exactly for this.
headroom sits at 0.9 because the countTokens estimate and the tokens you're actually billed for don't always line up. System instructions, tool declarations, and attachment metadata can ride along afterward. Aim for the ceiling exactly and you'll occasionally exceed it. In production, "occasionally" means "eventually, always."
I re-measured this on my own wallpaper app's bulk description generation. The job I'd assumed needed an enormous context turned out to fit in a few thousand tokens. I'd been waiting on the number two million without knowing my own. That, it turns out, was the preparation worth doing first.
Don't crash when you overflow
If the check fails, degrade rather than throw. Try splitting, then summarizing, then demoting — and only fail once none of those apply.
async function generateWithinBudget(contents, budget, opts = {}) { const model = budget.model; const check = await fits(model, contents, budget); if (check.ok) { return ai.models.generateContent({ model, contents }); } // 1) Splittable input: chunk it and process the pieces if (opts.splittable) { const chunks = splitByRatio(contents, check.totalTokens / check.ceiling); const parts = []; for (const c of chunks) parts.push(await generateWithinBudget(c, budget, opts)); return mergeParts(parts); } // 2) Reference material: compress it first if (opts.summarizable) { const digest = await summarize(contents, budget); return generateWithinBudget(digest, budget, { ...opts, summarizable: false }); } // 3) Neither applies: record it and fail explicitly throw new BudgetExceeded({ model, needed: check.totalTokens, ceiling: check.ceiling, });}
That third branch is the one I care about. Silently truncating and answering from a shortened input degrades quality while hiding the cause. If you truncate, say so in the record. If you can't answer, return that you can't. Working solo, you are the one who'll be debugging this in three months — the silence costs you personally.
The surest way to stop pre-seeding is to verify in CI that every model ID in your config is actually callable. Twenty lines does it.
// scripts/check-models.mjs — run this in CIimport { GoogleGenAI } from "@google/genai";const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });const declared = ["gemini-flash-latest", process.env.GEMINI_MODEL].filter(Boolean);const available = new Set();for await (const m of await ai.models.list()) available.add(m.name.replace("models/", ""));let failed = false;for (const id of declared) { const ok = available.has(id); console.log(`${ok ? "OK " : "MISS"} ${id}`); if (!ok) failed = true;}process.exit(failed ? 1 : 0);
models.list returns aliases alongside concrete models, so alias-based config passes fine. Read a headline, get excited, commit an early config — and CI goes red instead. All this does is move the red from a user's first request to a pull request page. That's a small thing, most days. On a day like today, it's the whole difference.
What to measure on the day it actually lands
Whenever gemini-3.5-pro does arrive, the day's work should be short if the layer above is already in place.
#
Step
What it tells you
1
models.get for inputTokenLimit
Whether the reported number holds. Only now is it confirmed
2
countTokens on your representative prompts
Whether the big window is needed, or splitting suffices
3
Small inputs: check quality and latency
Whether there's a reason to move off your current model
4
Scale input up in stages, measure cost and speed
Whether window size becomes value for your workload
Look at the ordering and you'll notice that steps 1 and 2 don't require the release at all. You can run them against gemini-3.5-flash today. What there was to do while waiting turned out to live not in the thing being waited on, but in how you wait.
Wrapping up
The reported date arrived and nothing shipped. Nothing on my side broke either — because the limit comes from the model rather than a constant, and because CI checks that the model IDs exist. Put the rumor in config, and today would have been an outage.
For a next step: call models.get once and log inputTokenLimit. That number is the only ceiling you're entitled to rely on today. If it disagrees with the constant in your code, you've found the thing to fix.
I won't claim I've gotten good at waiting. Every slipped date pulls my attention. But if you keep adding things that don't move to your side of the line, the next delay arrives a little more quietly.
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.