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
-
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.
-
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
Environment
uv run --with google-antigravityLocalOpenAIAgentConfig(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 nocontentand notool_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 400invalid message content type: <nil>, becausecontentcan be left out only whentool_callsis present. The retry never reaches the model, and the session ends withAntigravityExecutionError.I first saw this 400 three times in normal runs with
gemma4:12bandqwen3.6:35b, with the built-in tools enabled. Two of those runs logged this right before the 400: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
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.
The SDK sends
max_completion_tokens: 65535, but Ollama 0.34.2 maps onlymax_tokenstonum_predict, so the Modelfile limit applies.Run this script:
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 withfinish_reason: "length". The SDK logsThe maximum token limit was reachedand 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:
and the run ends:
The same happens without the proxy.
Additional Context
contentonly whentool_callsis present:openai/openai.goL640-L644 at v0.34.2. OpenAI's spec has the same rule for assistant messages: "Required unlesstool_callsorfunction_callis specified" (openapi.yamlL40473-L40475).{"role": "assistant"}returns 400, and{"role": "assistant", "content": ""}returns 200.content. Ollama accepts those becausetool_callsis present.