Using AI

Gemini API Returns an Empty Response: Fixing gemini-2.5-flash with thinkingBudget=0 and Batches of 5

Gemini API Returns an Empty Response: Fixing gemini-2.5-flash with thinkingBudget=0 and Batches of 5

Good morning, this is まさきん.

I sent 10 articles to the Gemini API in a single request, and the response came back with an empty body. Both the cause and the fix turned out to be explained in the official documentation, so I am leaving these notes here.

*This information is current as of the time of publication. Please check the official sites for the latest terms.

POINT
  • When a Gemini API response is empty, look at finishReason and usageMetadata first
  • According to the official docs, the output token limit (maxOutputTokens) applies to the total including thinking tokens
  • On gemini-2.5-flash, setting thinkingBudget to 0 turns thinking off (the range is 0 to 24576)
  • I set thinkingBudget=0 and split the batch of 10 into groups of 5, and it went through
  • The model sometimes invents deadlines that are not in the text, so I check dates against the source text in post-processing

What I was building

I built a small system that fetches new articles from a members-only site every day. For each article, I let Gemini decide whether it is a deal or not and write a summary.

The notifications go to our family chat. If everything gets posted, nobody reads it, so I narrowed it down to items judged to be deals.

The model is gemini-2.5-flash. Summarizing and classifying is routine work, so I figured a fast, cheap model would be a good fit.

I sent 10 at once and the body was empty

At first, I packed the 10 newest articles into one request. I wanted to reduce the number of calls.

But the body of the response came back empty. It did not fail with an error. A response arrived, it just had nothing in it.

An error raises an exception, so you notice. An empty response slips through quietly, which can be a bit of a nuisance. When you build this into a routine job, I think this is the first pitfall.

The cause: thinking also uses the output allowance

The official documentation explains that the Gemini 3 and 2.5 series are models that use a “thinking process.” The model reasons internally before it responds.

This thinking is on by default. The official term is “dynamic thinking,” and the amount adjusts automatically to the complexity of the request.

The key point is that this thinking consumes tokens too. The docs state that max_output_tokens is a limit that includes thinking tokens.

If the limit is reached in the middle of thinking, finish_reason becomes MAX_TOKENS. And the output is said to be either cut off partway or returned empty. When you ask the model to think through 10 judgments at once, I suspect the total of thinking plus output balloons easily.

Warning

The official documentation also has a note about billing. Even if the limit is reached and the output is empty, the thinking tokens that were generated are still billed. An empty response can mean you get no result and still pay for it.

A troubleshooting table for empty responses

There are several kinds of empty response. Based on the fields in the API reference, I sorted out where to look.

SymptomWhere to look in the responseLikely situationDirection of the fix
Body is empty or cut offfinishReason is MAX_TOKENSReached the specified maximum number of tokensReduce thinking, reduce items per request, review the limit
Body is empty and thinking is heavyusageMetadata.thoughtsTokenCount is largeThinking is using up the output allowanceLower thinkingBudget (2.5 series)
Body is empty for another reasonfinishReason is SAFETY or similarStopped for safety or other reasonsReview the input content and the prompt
JSON comes back but the content is offfinishReason is STOPThe format is correct but the values are wrongValidate the values in your application

A finishReason of STOP means the model stopped at a natural stopping point. MAX_TOKENS is defined as having reached the maximum number of tokens specified in the request.

So I think finishReason is the first thing to check when a response is empty. It may be safest to assume you will not use the body whenever it contains anything other than STOP.

How to read usageMetadata

The response comes with usageMetadata. The API reference describes it as follows.

FieldMeaning
promptTokenCountNumber of tokens in the prompt (input)
candidatesTokenCountTotal number of tokens in the generated response candidates
thoughtsTokenCountNumber of tokens used for thinking by a thinking model
totalTokenCountTotal of prompt + thinking + response candidates

What makes this easy to follow is that the total is defined as “prompt + thinking + response.” Thinking and the response are counted as separate fields.

If the thinking number is large and the response number is small, thinking may be eating the allowance. Logging these two makes it easier to trace the cause later.

How to set thinkingBudget

In the 2.5 series, you set the amount of thinking with a parameter called thinkingBudget. The official explanation is that it gives the model a guide for how many tokens to use for thinking.

Setting it to 0 disables thinking. Setting it to -1 gives you dynamic thinking. With the Python SDK, following the official example, it looks like this.

from google import genai
from google.genai import types

client = genai.Client()
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=prompt,
    config=types.GenerateContentConfig(
        thinking_config=types.ThinkingConfig(thinking_budget=0)
    ),
)

When calling via REST, you place thinkingConfig inside generationConfig. Within that, you pass thinkingBudget as a number.

That said, the docs include a caveat. Depending on the prompt, the model may go over or under the budget. It seems best to treat it as a guide rather than a strict cap.

thinkingBudget ranges by model

The range and the default differ by model. I copied the table from the official documentation, narrowed down to the main 2.5 series models.

ModelWhen not specifiedAllowed rangeDisabling thinkingDynamic thinking
2.5 ProDynamic thinking128 to 32768Not possible-1 (default)
2.5 FlashDynamic thinking0 to 24576Set 0-1 (default)
2.5 Flash LiteNo thinking512 to 24576Set 0-1

I think the important difference is that 2.5 Pro cannot stop thinking. If you carry over “the setting where I used 0 on Flash” to Pro as is, your assumptions may fall apart.

2.5 Flash Lite is a model that does not think unless you specify something. If all you need is summarizing and classifying, choosing this one is another way to go.

The Gemini 3 series uses thinkingLevel

In the official documentation as of publication, the way to specify this has changed from the Gemini 3 series onward. The recommended parameter is thinkingLevel. You specify it in steps such as minimal, low, medium, and high.

The 2.5 series, conversely, does not support thinkingLevel. The docs say to use thinkingBudget with the 2.5 series.

There were also notes on the Gemini 3 series. Gemini 3.1 Pro cannot disable thinking. Gemini 3 Flash and Flash-Lite are also described as not supporting thinking fully off. Even minimal is explained as “not a guarantee that thinking is off.”

In other words, once you move to a newer model, the trick of “set it to 0 and stop it” may no longer be available. It seems wise to expect that the settings will need rewriting when you switch.

Check first whether you can use the 2.5 series

The official model list carries a note about the 2.5 series. It says availability is limited to users who have actually used it in the past.

It also says the series is not deprecated and will continue to be offered via the API for the time being. However, new projects were advised to use the latest models.

My system runs on gemini-2.5-flash. If you are building something new from here, checking which models are available in your own project may be the first step.

Try the fee simulator and get 100 points, no sign-up required Once AI has lightened your daily information gathering, review your monthly phone bill with Rakuten Mobile too

Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.

Billing for thinking tokens

The pricing page states clearly that the output price “includes thinking tokens.” The entry for gemini-2.5-flash had the same wording.

The thinking guide has an explanation to the same effect. When thinking is on, the response price is the sum of output tokens and thinking tokens.

Even with a setting that returns only a summary of the thinking, billing is said to cover the entire original thinking tokens. It seems fair to understand that more may be counted than what you can see.

Routine jobs like summarizing and classifying run over and over every day. Each run is small, but it does not hurt to know that the thinking portion adds up.

How to specify structured output (JSON)

If your program is going to receive the judgment and the summary, having them returned as JSON is easy to handle. The Gemini API has a structured output mechanism for this.

In the API reference, you place two fields in generationConfig. You set responseMimeType to application/json. Along with that, you pass a schema in responseJsonSchema.

"generationConfig": {
  "responseMimeType": "application/json",
  "responseJsonSchema": {
    "type": "object",
    "properties": {
      "is_deal": {"type": "boolean"},
      "summary": {"type": "string"},
      "deadline": {"type": "string"}
    }
  }
}

The field names are examples I made up for illustration. The reference says responseMimeType is required when you pass a schema.

Note that the SDK guide also showed examples written slightly differently. I think it is best to recheck the official examples against the SDK version you are using.

The official best practices contain one important sentence. Structured output guarantees JSON that is syntactically correct, but it does not guarantee that the values are semantically correct. It says to validate them in your application before using them.

Splitting into groups of 5 got it through

I actually did two things. I set thinkingBudget to 0. And I split the batch of 10 into groups of 5.

With both in place, the body started coming back. I have not isolated whether either one alone would have been enough.

Point of comparison10 in one batchSplit into groups of 5
Number of callsFewerMore
Length of one outputLongerShorter
Risk of hitting the limitTends to be higherTends to be lower
Impact of a failureRedo everythingRedo only that group
Mixing up articlesCan happenEasier to reduce

A single batch looks more efficient. But when it fails, everything has to be redone. If you split it up, the scope of a failure stays within one group.

The best group size probably depends on the length of the articles and the shape of the output. Five is simply the number that happened to work for my use case.

Deadlines that are not in the text sometimes get invented

One more thing that troubled me was deadlines. Gemini sometimes produced a deadline that was not written in the article.

For deal notifications, a wrong deadline is a problem. Too early and you miss the opportunity, too late and you do not notice it has expired.

I think this connects to the caveat about structured output. Even if the JSON is shaped correctly, the values are not necessarily correct. When the schema has a “deadline” field, there may be a pull toward filling it in.

Discard deadlines whose date string is not in the text

As a countermeasure, I added post-processing. The idea is simple.

  1. Have Gemini return the deadline as a JSON field
  2. Check whether a string corresponding to that date exists in the original article text
  3. If it is not found in the text, discard the deadline field
  4. Send the notification as a deal with no deadline

I felt that just asking the model “do not make things up” was not reliable. So I check against the text on the program side.

This approach has a weakness too. If the notation in the text differs from the notation the model returns, it throws away correct deadlines as well. The design accepts that “no deadline is better than a wrong deadline.”

Key points when building this into a routine job

This episode helped me sort out how to look at putting generative AI into routine processing.

CHECK
  • Treat an empty response as an error (check the body length and finishReason)
  • Log usageMetadata so you can trace the breakdown of thinking and output
  • For tasks like summarizing and classifying, consider a setting that reduces thinking first
  • Do not cram too much into one request, and keep it to a size where only one group needs redoing on failure
  • Check the values the model returns against the original text before using them

The official best practices have a similar example. For simple tasks such as fact retrieval and classification, thinking should be minimal or low. Work that does not need difficult reasoning may not need deep thinking.

At work, too, I check numbers against their source before using them. Treating AI output the same way feels like the most settled approach to me.

I also wrote about having AI summarize each morning’s information in the article on having AI create my daily briefing. For permissions when running unattended, the article on why MCP gets denied with claude -p covers a related topic.

Common mistakes and pitfalls

Here are the points that are easy to trip over.

The second one is clearly warned about in the official docs as well. When you want to cut cost or latency, the guidance is to lower thinking rather than shrink the limit. Tightening the limit causes responses to be cut off.

The ones I stepped on were the first and the fifth. API usage and the paid plans for the app are separate matters. I summarized the app-side pricing in the article that sorts out Google AI Pro features and pricing.

What to check before you start

For anyone building a similar system, I put together a checklist.

I think the third and the sixth are especially effective. Empty responses and invented values both slip through quietly.

Frequently asked questions

Q. What should I look at first when a Gemini API response is empty?

I think it is best to look at finishReason. If it is MAX_TOKENS, the specified maximum number of tokens was reached. Checking thoughtsTokenCount as well tells you how much was used for thinking.

Q. Are there models where thinkingBudget cannot be set to 0?

In the official table, 2.5 Pro is listed as unable to disable thinking. Its range is 128 to 32768. The Gemini 3 series is specified with thinkingLevel, and some models do not support thinking fully off.

Q. What is the thinkingBudget range for gemini-2.5-flash?

In the official table it is 0 to 24576. If not specified, it uses dynamic thinking. Setting 0 disables it, and -1 means dynamic thinking.

Q. Are thinking tokens billed too?

Yes. The pricing page says the output price “includes thinking tokens.” Even if the limit is reached and the output is empty, the thinking that was generated is said to be billed.

Q. Will raising maxOutputTokens solve it?

If the response is being cut off because it hits the limit, giving it more headroom is one option. However, if thinking stays heavy, cost and latency go up. The official guidance points to lowering the thinking setting.

Q. Does structured output prevent mistakes in the content?

No. The docs say that what is guaranteed is only syntactically correct JSON. The semantic correctness of the values is not guaranteed. You need validation on the application side.

Q. How many items per group is best?

I do not think there is a fixed right answer. It depends on the length of the articles and the shape of the output. Splitting 10 into groups of 5 worked for me, but that is a result for my use case.

Q. Does turning thinking off lower the quality of the judgments?

I think it depends on the task. The official docs give classification and fact retrieval as examples where low thinking is enough. I have not compared the change in quality with numbers, so if you are concerned, trying it on your own articles seems best.

Official sources I referred to

Summary

The reason it came back empty was that thinking also uses the output allowance. Setting thinkingBudget to 0 and splitting into groups of 5 got it through.

Still, what settings can fix is only “whether something comes back.” Whether what comes back is correct had to be verified separately. The post-processing that checks deadlines against the text exists for that purpose.

If you have generative AI in a routine job, why not take a look at how you are handling empty responses?

Try the fee simulator and get 100 points, no sign-up required Once systems have cut down your chores, check your phone and data costs with Rakuten Mobile too

Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.

This article contains affiliate advertising. If you sign up for a product or service through a link on this site, we may receive compensation from the partner company. The site operator is also an employee of Rakuten Group and may receive compensation through an employee referral program. The content and opinions here are based on the operator’s own experience and research regardless of any advertising relationship, but please read with the above in mind. See the Disclaimer & Affiliate Disclosure for details. Disclaimer & Affiliate Disclosure

This article includes machine-translated content. Please check the official Rakuten Mobile multilingual page for exact terms.

If you have concerns about Rakuten Mobile coverage or signal quality, you can consult through the official signal improvement request form .

ABOUT THE AUTHOR
まさきん

Rakuten Group employee · Digital Marketer (holds a Financial Planner qualification)

In his early 40s, part of a dual-income household with four kids. Works as a digital marketer at Rakuten Group, while also using his Financial Planner (FP) qualification to focus on household finances and building assets.

View Profile

Related Articles

RELATED
Gemini (Google AI Pro) Family Sharing Covers Up to 5 Family Members: Setup Steps and Scope Using AI

Gemini (Google AI Pro) Family Sharing Covers Up to 5 Family Members: Setup Steps and Scope

Google AI Pro can be shared with up to 5 family members besides the manager. The Gemini app and storage are shared, while YouTube Premium Lite is for the manager only. Flow limits are shared across the family. I went through the official help to sort out the setup steps and how child accounts are handled.

2026.10.13 · 11 min read
3 Reasons Your AI Subscription Billing Date Does Not Match Your Budget: Claude Upgrades Change It Using AI

3 Reasons Your AI Subscription Billing Date Does Not Match Your Budget: Claude Upgrades Change It

If you upgrade Claude to Max mid-month, your billing cycle resets and that day becomes your new billing date. The yen conversion of dollar charges is set when the data reaches your card company, and statements and budgeting apps lag by a few days. Here are 5 steps to decide the month using the receipt email as your source of truth.

2026.10.13 · 12 min read
Turning Unprocessed freee Accounting Transactions into a Monthly Reconciliation Run: 7:30 on the 5th, 3 Categories, and a Cap of 15 Items Handed to a Human Using AI

Turning Unprocessed freee Accounting Transactions into a Monthly Reconciliation Run: 7:30 on the 5th, 3 Categories, and a Cap of 15 Items Handed to a Human

About 18 unprocessed transactions sat untouched in freee Accounting across 6 weekly checks. Here is my design for a reconciliation run on my home Mac at 7:30 a.m. on the 5th of each month, and where I drew the line: one monthly task for the items that can be confirmed, and one task each, capped at 15, for the ones that need a human decision.

2026.10.12 · 13 min read
Switch to Rakuten Mobile, get up to 14,000 points Try the fee simulator and get 100 points, no sign-up required →
Apply Now →