GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-05-30Advanced

Propagating a Time Budget Through a Multi-Stage Gemini Pipeline

A field memo on killing DEADLINE_EXCEEDED errors in an in-app help search by carrying a single request-wide deadline through the embed, search, and generate stages — sizing maxOutputTokens from the remaining budget and reserving a fallback budget so a breach returns a partial answer instead of an error.

gemini-api277deadline-propagationlatency-budgetpipeline-designindie-development5production140

Premium Article

In the first week after shipping an in-app help search, I kept staring at the number of DEADLINE_EXCEEDED lines in the logs. When a user types a question, a four-stage pipeline runs: input normalization, embedding, vector search, and answer generation with Gemini. The perceived target is four seconds. The median was a comfortable 1.6 seconds, yet the top 2% of requests would time out — throwing away all of the upstream work. I am Masaki Hirokawa, an indie developer and artist. Across the 50-million-download app business I have run since 2014 and the six sites of Dolice Labs, I kept seeing the same failure mode: the final generation stage runs out of time. This is a memo on how I fixed it by propagating a single request-wide deadline through every stage.

My grandfather was a temple carpenter, and before he started cutting joints he always wrote out the plan for the day on paper: how much time each timber would get, decided up front. Writing this code, I realized deadline propagation is exactly that — handing out the time plan before the work begins.

Why Only the Final Stage Always Times Out

When each stage in a pipeline owns an independent timeout, upstream delays get pushed onto downstream stages. In my help search, the embedding and search stages each carried a "2 seconds max" timeout. Normally both finish in 0.4 seconds, but for the first few requests of the morning, before the vector DB warms up, search can take 1.8 seconds. That leaves the generation stage only a second or so against the four-second target. Because Gemini's generation time scales with output tokens, the moment it tries to produce a slightly longer answer, the API blows past the deadline and DEADLINE_EXCEEDED wipes out everything.

The real problem is that each stage only knows its own local timeout. Nobody knows how many seconds are left for the request as a whole. This is a trap common to any design that chains several I/O calls in series, not just Gemini.

StageNormal (p50)Slow (p95)Local cap
Input normalize0.05s0.1s0.5s
Embedding0.35s0.9s2.0s
Vector search0.4s1.8s2.0s
Generation0.8s3.2s2.0s

Add up the local caps and you get 6.5 seconds against a four-second target. The caps simply do not agree with each other. As long as every stage believes it may use two seconds, the downstream stages will always pay for an upstream delay.

Carry the Deadline as an Absolute Time, Not a Relative One

My first mistake was passing each stage a relative budget — "you may use this many milliseconds." That quietly inflates, because the queuing and small processing steps between stages never get counted. The correct approach is to fix one absolute deadline at the start of the request and carry it through every stage. Each stage computes for itself, right before it runs, how many milliseconds remain.

// deadline.ts — create exactly one of these per request
export class Deadline {
  private readonly at: number; // absolute time (epoch ms)
 
  private constructor(at: number) {
    this.at = at;
  }
 
  /** Build from the SLA at the start of the request */
  static fromNow(budgetMs: number): Deadline {
    return new Deadline(Date.now() + budgetMs);
  }
 
  /** Milliseconds left until the deadline (never negative) */
  remaining(): number {
    return Math.max(0, this.at - Date.now());
  }
 
  /** Can this stage meet its minimum budget? */
  hasBudgetFor(stageMinMs: number): boolean {
    return this.remaining() >= stageMinMs;
  }
 
  /** Build the AbortSignal for an API call from the remaining budget.
   *  reserveMs is the time we always keep in hand for a fallback. */
  signal(reserveMs = 0): AbortSignal {
    const ms = Math.max(0, this.remaining() - reserveMs);
    return AbortSignal.timeout(ms);
  }
}

The key is signal(reserveMs). The Gemini SDK won't propagate a deadline for you, so you pass an explicit AbortSignal to each call, and its width is "the remaining budget minus the fallback reserve." That reserve is the foundation of the "return a partial answer instead of an error" trick below.

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 50-line TypeScript Deadline object that carries one absolute deadline through every stage of the pipeline
A formula that derives maxOutputTokens from the remaining budget, plus how to set each stage's minimum budget from measured p95
A fallback-budget pattern that returns a partial answer instead of an error on a deadline breach, with real numbers from running six apps
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

API / SDK2026-05-20
Evolving Gemini API Structured Output Schemas in Production — Design Notes from an Indie App
How I rebuilt the JSON contract layer for a Gemini-powered recommendation feature in a long-running indie app — Dual-Emit, Sunset protocol, and a Python compatibility checker.
API / SDK2026-07-18
The Same gemini-flash-latest Pointed to Different Models in Different Regions
Alias resolution rolls out region by region. Send the same gemini-flash-latest to Tokyo and us-central1 and, for a few days, different models answer. Here is why that quietly invalidates your comparisons, and how a one-shot probe catches it.
API / SDK2026-07-15
Sample what you already accepted — an audit budget that catches silent quality drift
A confidence gate only ever looks at output the model hesitated on. Silent drift sinks into the batch that sailed through. Working from a fixed 30-minutes-a-day review budget, this walks through deriving detection time from the binomial, reallocating the same budget across risk strata, and catching slow decay with a cumulative monitor.
📚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 →