Skip to content

Empty model turn is replayed without content, and Ollama rejects the retry with HTTP 400 #225

Description

@Sora-bluesky

Environment

  • SDK version: 0.1.18
  • Python version: 3.14.7
  • OS / platform: Windows 11 x86_64 (10.0.26200)
  • Install method: uv run --with google-antigravity
  • Model server: Ollama 0.34.2 through LocalOpenAIAgentConfig(base_url="http://localhost:11434/v1")

Description

When the model returns a turn with no text and no tool calls, the SDK keeps that turn in the history as {"role": "assistant"}, with no content and no tool_calls. It then adds its retry notice as a user message and sends the history again. Ollama's OpenAI-compatible API rejects the bare assistant message with HTTP 400 invalid message content type: <nil>, because content can be left out only when tool_calls is present. The retry never reaches the model, and the session ends with AntigravityExecutionError.

I first saw this 400 three times in normal runs with gemma4:12b and qwen3.6:35b, with the built-in tools enabled. Two of those runs logged this right before the 400:

Model produced invalid output. ("model output error: model output must contain either output text or tool calls, these cannot both be empty, please try again")

The third run logged only the 400. I had no capture of those requests, so I put a logging proxy between the SDK and Ollama and forced an empty turn with a 4-token output limit. The steps below reproduced the 400 in all three of my runs, one with a Japanese prompt and two with the English prompt shown here.

Steps to Reproduce

  1. Create a model whose output stops after 4 tokens. Gemma 4 starts with reasoning, so the turn ends with no text and no tool calls.

    FROM gemma4:12b
    PARAMETER num_ctx 65536
    PARAMETER num_predict 4
    
    ollama create gemma4-empty -f Modelfile
    

    The SDK sends max_completion_tokens: 65535, but Ollama 0.34.2 maps only max_tokens to num_predict, so the Modelfile limit applies.

  2. Run this script:

    import asyncio
    
    from google.antigravity import Agent, LocalOpenAIAgentConfig
    
    
    async def main():
        config = LocalOpenAIAgentConfig(model="gemma4-empty", base_url="http://localhost:11434/v1")
        async with Agent(config) as agent:
            response = await agent.chat("Write quicksort in Python")
            async for text in response:
                print(text, end="", flush=True)
    
    
    asyncio.run(main())

Expected Behavior

The retry reaches the model. An assistant turn with no text and no tool calls is either left out of the history or sent with "content": "".

Actual Behavior

The first request streams back only reasoning, {"role": "assistant", "content": "", "reasoning": "The"}, and ends with finish_reason: "length". The SDK logs The maximum token limit was reached and sends a second request. This is that request as the proxy recorded it, with the system prompt, the 13 tool definitions and the local time left out:

{
  "model": "gemma4-empty",
  "stream": true,
  "tool_choice": "auto",
  "max_completion_tokens": 65535,
  "messages": [
    {"role": "system", "content": "<SDK system prompt, 5148 characters>"},
    {"role": "user", "content": "<USER_REQUEST>\nWrite quicksort in Python\n</USER_REQUEST>\n<ADDITIONAL_METADATA>\nThe current local time is: <omitted>.\n</ADDITIONAL_METADATA>"},
    {"role": "assistant"},
    {"role": "user", "content": "Error: There was a problem. \nError Message: model output error: max tokens limit reached (generation or context length exceeded). \nRetries remaining: 4"}
  ]
}

Ollama answers with:

HTTP 400
{"error":{"message":"invalid message content type: \u003cnil\u003e","type":"invalid_request_error","param":null,"code":null}}

and the run ends:

WARNING:root:System step error (HTTP 0): The maximum token limit was reached. ("model output error: max tokens limit reached (generation or context length exceeded).")
WARNING:root:System step error (HTTP 2): Agent execution terminated due to error. ("agent executor error: HTTP request failed with status 400: {\"error\":{\"message\":\"invalid message content type: \\u003cnil\\u003e\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":null}}")
google.antigravity.types.AntigravityExecutionError: HTTP request failed with status 400: {"error":{"message":"invalid message content type: \u003cnil\u003e","type":"invalid_request_error","param":null,"code":null}}

The same happens without the proxy.

Additional Context

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions