Skip to content

gemini-3.5-flash-lite rejects zero budget at thinking mode #2936

Open

Description

@phenobarbital

With the latest version of python-genai, gemini-3.5-flash-lite is not able to work when "thinkingBudget=0", with "thinkingBudget=None" works, but other models working fine with a budget of zero.

But, using a simple script with python httpx (no google-genai involved) I got this:

  ┌─────────────────────────────────────┬─────────────────┐
  │        gemini-3.5-flash-lite        │ Raw REST result │
  ├─────────────────────────────────────┼─────────────────┤
  │ thinkingBudget: 0                   │ 400             │
  ├─────────────────────────────────────┼─────────────────┤
  │ omitted                             │ 200 OK          │
  ├─────────────────────────────────────┼─────────────────┤
  │ -1 (dynamic)                        │ 200 OK          │
  ├─────────────────────────────────────┼─────────────────┤
  │ 512                                 │ 200 OK          │
  ├─────────────────────────────────────┼─────────────────┤
  │ (control) gemini-2.5-flash-lite + 0 │ 200 OK          │
  └─────────────────────────────────────┴─────────────────┘

I tested all other models, including 3.8 family without issues, this is the only model with this problem.

Activity

  1. added
    type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.
    priority: p2Moderately-important priority. Fix may not be included in next release.
    on Sep 3, 2026
  2. Venkaiahbabuneelam commented on Sep 3, 2026

    @Venkaiahbabuneelam

    Hi @phenobarbital, Thanks for reaching out us!
    To help us understand the issue, could you please share a code snippet of what you've tried so far?
    Thanks

  3. phenobarbital commented on Sep 4, 2026

    @phenobarbital
    Author

    @Venkaiahbabuneelam yes, sure!

    I tested directly using this snippet:

    import asyncio
    import json
    import os
    import aiohttp
    from google import genai
    from google.genai import types
    from parrot.conf import config
    
    API_KEY = config.get("GOOGLE_API_KEY") or os.getenv("GOOGLE_API_KEY") or os.getenv("GEMINI_API_KEY")
    
    async def test_raw_rest(session, model, thinking_budget, label):
        url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={API_KEY}"
        generation_config = {}
        if thinking_budget is not None:
            generation_config["thinkingConfig"] = {"thinkingBudget": thinking_budget}
        
        payload = {
            "contents": [{"parts": [{"text": "Say hello in 5 words."}]}],
        }
        if generation_config:
            payload["generationConfig"] = generation_config
    
        try:
            async with session.post(url, json=payload) as resp:
                status = resp.status
                text = await resp.text()
                try:
                    data = json.loads(text)
                    if status == 200:
                        cand = data.get("candidates", [{}])[0]
                        content = cand.get("content", {}).get("parts", [{}])[0].get("text", "")
                        print(f"[REST] {model} | {label} -> HTTP {status} OK: {content.strip()[:60]}")
                    else:
                        err = data.get("error", {}).get("message", text[:100])
                        print(f"[REST] {model} | {label} -> HTTP {status} ERROR: {err}")
                except Exception:
                    print(f"[REST] {model} | {label} -> HTTP {status} Raw: {text[:100]}")
                return status
        except Exception as e:
            print(f"[REST] {model} | {label} -> EXCEPTION: {e}")
            return None
    
    async def test_sdk(client, model, thinking_budget, thinking_level, label):
        th_cfg = None
        if thinking_budget is not None or thinking_level is not None:
            kwargs = {}
            if thinking_budget is not None:
                kwargs["thinking_budget"] = thinking_budget
            if thinking_level is not None:
                kwargs["thinking_level"] = thinking_level
            th_cfg = types.ThinkingConfig(**kwargs)
    
        gen_cfg = types.GenerateContentConfig(thinking_config=th_cfg) if th_cfg else None
    
        try:
            resp = await client.aio.models.generate_content(
                model=model,
                contents="Say hello in 5 words.",
                config=gen_cfg,
            )
            print(f"[SDK]  {model} | {label} -> SUCCESS: {resp.text.strip()[:60] if resp.text else 'EMPTY'}")
            return 200
        except Exception as e:
            print(f"[SDK]  {model} | {label} -> EXCEPTION: {type(e).__name__}: {e}")
            return str(e)
    
    async def main():
        print("=" * 60)
        print("TESTING GEMINI-3.5-FLASH-LITE & CONTROL MODELS")
        print(f"SDK version: {genai.__version__}")
        print("=" * 60)
    
        models_to_test = [
            "gemini-3.5-flash-lite",
            "gemini-2.5-flash-lite",
        ]
    
        async with aiohttp.ClientSession() as session:
            print("\n--- 1. RAW REST (v1beta) ---")
            for m in models_to_test:
                for tb, label in [
                    (None, "omitted"),
                    (0, "thinkingBudget: 0"),
                    (-1, "thinkingBudget: -1"),
                    (512, "thinkingBudget: 512"),
                ]:
                    await test_raw_rest(session, m, tb, label)
    
        print("\n--- 2. GOOGLE-GENAI SDK ---")
        client = genai.Client(api_key=API_KEY)
        for m in models_to_test:
            for tb, tl, label in [
                (None, None, "omitted"),
                (0, None, "thinking_budget=0"),
                (-1, None, "thinking_budget=-1"),
                (512, None, "thinking_budget=512"),
                (None, "MINIMAL", "thinking_level='MINIMAL'"),
                (None, "LOW", "thinking_level='LOW'"),
            ]:
                await test_sdk(client, m, tb, tl, label)
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    When is called with plain aiohttp calls I got this:

    --- 1. RAW REST (v1beta) ---
    [REST] gemini-3.5-flash-lite | omitted -> HTTP 200 OK: Hello, how are you today?
    [REST] gemini-3.5-flash-lite | thinkingBudget: 0 -> HTTP 400 ERROR: Request contains an invalid argument.
    [REST] gemini-3.5-flash-lite | thinkingBudget: -1 -> HTTP 200 OK: Hi, nice to meet you.
    [REST] gemini-3.5-flash-lite | thinkingBudget: 512 -> HTTP 200 OK: Hello there, how are you?
    [REST] gemini-2.5-flash-lite | omitted -> HTTP 200 OK: Hello there, how are you?
    [REST] gemini-2.5-flash-lite | thinkingBudget: 0 -> HTTP 200 OK: Hello, it's great to see you.
    [REST] gemini-2.5-flash-lite | thinkingBudget: -1 -> HTTP 200 OK: Hello! Wishing you a nice day.
    [REST] gemini-2.5-flash-lite | thinkingBudget: 512 -> HTTP 200 OK: Hello, how are you doing?
    
    

    but with google-genai (version: 2.18.1) I got:

    [SDK]  gemini-3.5-flash-lite | omitted -> SUCCESS: Hello there, how are you?
    [INFO] 2026-09-04 12:34:58,055 google_genai.models(models.py:8757) :: AFC is enabled with max remote calls: 10.
    [SDK]  gemini-3.5-flash-lite | thinking_budget=0 -> EXCEPTION: ClientError: 400 INVALID_ARGUMENT. {'error': {'code': 400, 'message': 'Request contains an invalid argument.', 'status': 'INVALID_ARGUMENT'}}
    

    the unrecognized thinkingBudget=0 only affects gemini-3.5-flash-lite, any other flash models (e.g. 3.8) or older (like 3.1 or 2.5) are not affected by this issue.

    I was using thinkingBudget=0 on any model but gemini-3.5-flash-lite.

    as conclusion:

    Key Inconsistencies for the Incident Report

    1. SDK Docstring vs Reality:
      The google-genai SDK docstring (types.py:13606) states:
      │ "Starting from Gemini 3.5 models, the old thinking_budget will no longer be supported and will result in a user error if set. Instead, users should use the thinking_level parameter to control the maximum thinking depth."
      However, thinking_budget is not completely unsupported on 3.5: -1 and 512 succeed without errors. Only 0 is rejected.
    2. No Mechanism to Disable Thinking:
      types.ThinkingLevel only defines MINIMAL, LOW, MEDIUM, and HIGH. It does not provide an OFF or NONE level. Since thinking_budget=0 raises a 400 error, developers migrating from 2.5 have no way to turn thinking off.
    3. Opaque Error Message:
      Pro models explicitly explain invalid budget configurations (e.g. "Budget 0 is invalid. This model only works in thinking mode"). gemini-3.5-flash-lite only outputs the generic message: "Request contains an invalid argument.")
  4. kkorpal commented on Sep 8, 2026

    @kkorpal

    Hi @phenobarbital, thanks for the fantastic report and test script! We ran it and perfectly reproduced the 400 error with thinkingBudget=0 on gemini-3.5-flash-lite, as well as the missing OFF state in the new thinking levels. I've escalated this straight to the engineering and API teams to get both issues sorted, thanks again for flagging this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions