You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Selecting a Google model and starting a run of a harness agent (create_harness_agent) over GeminiChatClient crashes the stream during request preparation — before or at the first model call — for two independent, Gemini-specific reasons. The same agent and tool set run unchanged on Ollama, the OpenAI-compatible clients, and Azure Foundry.
Tool parameter schemas._prepare_tools builds each types.FunctionDeclaration with parameters=tool.parameters(). That field is a google.genaitypes.Schema (an OpenAPI-3.0 subset) that rejects the JSON-Schema keywords the harness's own tools emit — $ref/$defs (the built-in todos_add(todos: list[_TodoAddItemSchema]) tool) and oneOf (any tool with an optional/union object argument). Pydantic raises ValidationError at declaration-build time, so no request is ever sent.
Built-in web search mixed with function calling. The harness adds a web-search tool by default (create_harness_agent(disable_web_search=False)); for a client that supports it, that becomes client.get_web_search_tool(). On Gemini that is a native types.Tool(google_search=...)built-in. Gemini rejects a request that mixes a built-in tool with function declarations unless tool_config.include_server_side_tool_invocations is enabled — which the client never sets.
Either defect alone is fatal; 1 fires first, so 2 is only reachable after 1 is fixed. Both are Gemini-only: Ollama doesn't implement SupportsWebSearchTool, and the OpenAI-compatible/Foundry clients accept $ref/$defs/oneOf schemas.
Verified against: upstream main @ 0260ea0e (2026-07-03); installed agent-framework-gemini 1.0.0a260630, agent-framework-core 1.10.0, google-genai 1.75.0, pydantic 2.13.4 Provider under test: Gemini Developer API (Google AI Studio), api_key auth, model gemini-3.5-flash (also reproduces on gemini-3.1-flash-lite; independent of model)
2. Symptoms
User-visible (any AG-UI frontend; observed with CopilotKit): the user picks a Google model, sends a message, and the reply is a generic "An internal error has occurred while streaming events." No tokens ever stream.
Backend traceback (defect #1), captured via OpenTelemetry logs:
File ".../agent_framework_gemini/_chat_client.py", line 915, in _prepare_tools
types.FunctionDeclaration(
pydantic_core._pydantic_core.ValidationError: 2 validation errors for FunctionDeclaration
parameters.properties.todos.items.$ref
Extra inputs are not permitted [type=extra_forbidden, input_value='#/$defs/_TodoAddItemSchema', ...]
parameters.$defs
Extra inputs are not permitted [type=extra_forbidden, input_value={'_TodoAddItemSchema': {...}}, ...]
A tool with an optional object argument fails the same way on parameters.properties.<arg>.oneOf.
Backend traceback (defect 2, once 1 is fixed):
File ".../agent_framework_gemini/_chat_client.py", line 541, in _stream
async for chunk in await self._genai_client.aio.models.generate_content_stream(...)
google.genai.errors.ClientError: 400 Bad Request. {
"error": { "code": 400,
"message": "Please enable tool_config.include_server_side_tool_invocations to use Built-in tools with Function calling.",
"status": "INVALID_ARGUMENT" } }
3. Reproduction
Both are reproducible in isolation against the live Gemini Developer API — no framework harness required.
3.1 Defect 1 — tool schema ($ref/$defs, oneOf)
fromtypingimportTypedDictfromtyping_extensionsimportNotRequiredfromagent_frameworkimporttoolfromgoogle.genaiimporttypesclass_TodoAddItemSchema(TypedDict): # must be module-scope for the forward reftitle: strdescription: NotRequired[str]
@tool(name="todos_add")asyncdeftodos_add(todos: list[_TodoAddItemSchema]) ->str:
"Add todos."return"ok"schema=todos_add.parameters() # -> {"$defs": {...}, "properties": {"todos": {"items": {"$ref": "#/$defs/_TodoAddItemSchema"}, ...}}, ...}types.FunctionDeclaration(name="todos_add", description="x", parameters=schema) # ValidationError: $ref / $defs "Extra inputs are not permitted"# oneOf variant (optional free-form object arg) fails the same way:oneof= {"type": "object", "properties": {"args": {"oneOf": [{"type": "object"}, {"type": "null"}]}}}
types.FunctionDeclaration(name="t", description="x", parameters=oneof) # ValidationError: parameters.properties.args.oneOf
This is exactly what create_harness_agent triggers: its TodoProvider.todos_add tool produces the $ref/$defs schema above.
3.2 Defect 2 — built-in tool + function calling
importasynciofromgoogleimportgenaifromgoogle.genaiimporttypesclient=genai.Client(api_key="<GEMINI_API_KEY>") # Developer API / Google AI Studiofn=types.Tool(function_declarations=[types.FunctionDeclaration(
name="get_weather", description="Get weather",
parameters_json_schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]})])
search=clientandtypes.Tool(google_search=types.GoogleSearch()) # what the harness auto-adds on Geminiauto=types.FunctionCallingConfig(mode="AUTO")
asyncdefgo(tool_config):
stream=awaitclient.aio.models.generate_content_stream(
model="gemini-3.5-flash", contents="What's the weather in Paris?",
config=types.GenerateContentConfig(tools=[search, fn], tool_config=tool_config))
asyncfor_instream: passasyncio.run(go(types.ToolConfig(function_calling_config=auto))) # 400 INVALID_ARGUMENT (as above)asyncio.run(go(types.ToolConfig(function_calling_config=auto, include_server_side_tool_invocations=True))) # OK
End-to-end: run create_harness_agent(..., disable_web_search=False) (the default) over GeminiChatClient with any function tools and send any prompt.
4.1 Defect 1 — _prepare_tools passes raw JSON Schema to the OpenAPI-subset parameters
RawGeminiChatClient._prepare_tools (installed build _chat_client.py:883) builds declarations directly from the tool's JSON schema:
types.FunctionDeclaration(
name=tool.name,
description=tool.descriptionor"",
parameters=tool.parameters(), # line ~915: raw JSON Schema -> types.Schema
)
types.FunctionDeclaration.parameters is typed types.Schema, the OpenAPI-3.0 subset, which disallows $ref, $defs, definitions, and oneOf (only anyOf is modelled, and unions must be inlined). Pydantic rejects the extra keys with extra_forbidden. Tools whose parameters reference a nested model (Pydantic/TypedDict → $ref + $defs) or carry a union (oneOf) therefore can never be declared. This is a well-known Gemini-API constraint that peer SDKs hit and solve by flattening or by using JSON-Schema passthrough (see §6).
flowchart LR
A["tool.parameters()<br/>JSON Schema w/ $ref/$defs or oneOf"] --> B["types.FunctionDeclaration(parameters=...)"]
B --> C{"types.Schema<br/>(OpenAPI-3.0 subset)"}
C -->|"$ref / $defs / oneOf"| D["pydantic ValidationError<br/>extra_forbidden"]
style D fill:#fdd,stroke:#c00
Loading
4.2 Defect 2 — the harness adds a built-in tool the client never flags for
create_harness_agent (core _harness/_agent.py, disable_web_search: bool = False by default) appends a web-search tool when the client supports it:
ifnotdisable_web_search:
ifisinstance(client, SupportsWebSearchTool):
assembled_tools.append(client.get_web_search_tool()) # Gemini -> types.Tool(google_search=...)else:
logger.warning("Web search tool not available: client %r does not implement SupportsWebSearchTool. ...")
GeminiChatClient implements SupportsWebSearchTool, so a native google_search built-in is added and passed through _prepare_tools unchanged, alongside the function declarations. On the Gemini Developer API, mixing a built-in tool with function declarations requires tool_config.include_server_side_tool_invocations=True; the client's _prepare_config/_prepare_tool_config never set it, so the API returns 400. (Ollama's client is not SupportsWebSearchTool, so no built-in is added — hence the crash is Gemini-only.)
Platform nuance (confirmed by peer reports, §6): Vertex AI allows the combination without the flag and rejects the field as unknown, so the flag must be set only on the Developer-API path (the client already tracks this as self._vertexai).
5. Proposed upstream fix
Both fixes live in agent-framework-gemini; neither requires changes to the harness or other providers.
Declare tools with parameters_json_schema instead of parameters.google-genai (≥ the pinned 1.75.0) exposes FunctionDeclaration.parameters_json_schema, forwarded to the API as parametersJsonSchema, which the model interprets as full JSON Schema ($ref/$defs, oneOf/anyOf, nullable unions). This offloads the whole compatibility surface to the API rather than lossily transcribing into types.Schema by hand. Exactly one of parameters / parameters_json_schema may be set. (Google's own guidance for complex schemas is to use parametersJsonSchema.) A stricter alternative — flatten $ref/$defs and map oneOf→anyOf into types.Schema — is more code and still lossy for constructs the subset can't express.
Enable include_server_side_tool_invocations when a built-in tool is present. In _prepare_config (after building tools), when any entry is a native types.Tool and the client is not in Vertex mode (self._vertexai), set tool_config.include_server_side_tool_invocations = True (creating the ToolConfig if absent). This is Google's prescribed fix and preserves web-search grounding (a documented, intended capability — the framework's own surfaces render web_search/search_tool_result content).
Optional hardening: _prepare_tools could dedupe identical native built-ins (the harness path can present two google_search tools); the API tolerates the duplicate with the flag on, so this is cosmetic.
6. Prior art / duplicate check (2026-07-07)
No existing microsoft/agent-framework issue reports either defect. Adjacent MAF issues found (different failure modes): #3434 (Python, Gemini streaming text lost with usage — closed), #3390 (.NET, Gemini image upload 400), #5490 (.NET tool calling), #6467 (.NET structured-output vs tool calls).
The underlying Gemini-API behaviors are corroborated across the ecosystem, which both confirms the diagnosis and points at the accepted fixes:
Combining provider-defined (built-in) tools with function tools on Gemini 3: Google AI Studio needs include_server_side_tool_invocations=true; Vertex AI works without it and errors if set — directly validates defect 2's fix and the Vertex gating
7. Verification
Defect 1: the §3.1 repro raises ValidationError on parameters=; the identical schema on parameters_json_schema= builds cleanly and is accepted by the live API (generate_content_stream streams) for both the $ref/$defs and oneOf shapes.
Defect 2: the §3.2 repro returns the 400 without the flag and succeeds with it, against gemini-3.5-flash on the Developer API (also with a duplicated google_search tool).
End-to-end: with the companion app-side shim applied (both fixes), a harness agent on gemini-3.5-flash over AG-UI/CopilotKit completed a full turn — todos_add and other tools executed, an artifact was saved, and the plan streamed — with zero streaming errors and clean telemetry (0 ValidationError, 0 400s). Full app test suite: 128 passed, including 10 new tests covering both crash shapes and both fixes.
Description
Selecting a Google model and starting a run of a harness agent (
create_harness_agent) overGeminiChatClientcrashes the stream during request preparation — before or at the first model call — for two independent, Gemini-specific reasons. The same agent and tool set run unchanged on Ollama, the OpenAI-compatible clients, and Azure Foundry.Tool parameter schemas.
_prepare_toolsbuilds eachtypes.FunctionDeclarationwithparameters=tool.parameters(). That field is agoogle.genaitypes.Schema(an OpenAPI-3.0 subset) that rejects the JSON-Schema keywords the harness's own tools emit —$ref/$defs(the built-intodos_add(todos: list[_TodoAddItemSchema])tool) andoneOf(any tool with an optional/union object argument). Pydantic raisesValidationErrorat declaration-build time, so no request is ever sent.Built-in web search mixed with function calling. The harness adds a web-search tool by default (
create_harness_agent(disable_web_search=False)); for a client that supports it, that becomesclient.get_web_search_tool(). On Gemini that is a nativetypes.Tool(google_search=...)built-in. Gemini rejects a request that mixes a built-in tool with function declarations unlesstool_config.include_server_side_tool_invocationsis enabled — which the client never sets.Either defect alone is fatal; 1 fires first, so 2 is only reachable after 1 is fixed. Both are Gemini-only: Ollama doesn't implement
SupportsWebSearchTool, and the OpenAI-compatible/Foundry clients accept$ref/$defs/oneOfschemas.Verified against: upstream
main@0260ea0e(2026-07-03); installedagent-framework-gemini 1.0.0a260630,agent-framework-core 1.10.0,google-genai 1.75.0,pydantic 2.13.4Provider under test: Gemini Developer API (Google AI Studio),
api_keyauth, modelgemini-3.5-flash(also reproduces ongemini-3.1-flash-lite; independent of model)2. Symptoms
User-visible (any AG-UI frontend; observed with CopilotKit): the user picks a Google model, sends a message, and the reply is a generic "An internal error has occurred while streaming events." No tokens ever stream.
Backend traceback (defect #1), captured via OpenTelemetry logs:
A tool with an optional object argument fails the same way on
parameters.properties.<arg>.oneOf.Backend traceback (defect 2, once 1 is fixed):
3. Reproduction
Both are reproducible in isolation against the live Gemini Developer API — no framework harness required.
3.1 Defect 1 — tool schema (
$ref/$defs,oneOf)This is exactly what
create_harness_agenttriggers: itsTodoProvider.todos_addtool produces the$ref/$defsschema above.3.2 Defect 2 — built-in tool + function calling
End-to-end: run
create_harness_agent(..., disable_web_search=False)(the default) overGeminiChatClientwith any function tools and send any prompt.Code Sample
Error Messages / Stack Traces
Package Versions
agent-framework-gemini: 1.0.0a260630, agent-framework-core: 1.10.0
Python Version
Python 3.12
Additional Context
4. Root cause
4.1 Defect 1 —
_prepare_toolspasses raw JSON Schema to the OpenAPI-subsetparametersRawGeminiChatClient._prepare_tools(installed build_chat_client.py:883) builds declarations directly from the tool's JSON schema:types.FunctionDeclaration.parametersis typedtypes.Schema, the OpenAPI-3.0 subset, which disallows$ref,$defs,definitions, andoneOf(onlyanyOfis modelled, and unions must be inlined). Pydantic rejects the extra keys withextra_forbidden. Tools whose parameters reference a nested model (Pydantic/TypedDict →$ref+$defs) or carry a union (oneOf) therefore can never be declared. This is a well-known Gemini-API constraint that peer SDKs hit and solve by flattening or by using JSON-Schema passthrough (see §6).flowchart LR A["tool.parameters()<br/>JSON Schema w/ $ref/$defs or oneOf"] --> B["types.FunctionDeclaration(parameters=...)"] B --> C{"types.Schema<br/>(OpenAPI-3.0 subset)"} C -->|"$ref / $defs / oneOf"| D["pydantic ValidationError<br/>extra_forbidden"] style D fill:#fdd,stroke:#c004.2 Defect 2 — the harness adds a built-in tool the client never flags for
create_harness_agent(core_harness/_agent.py,disable_web_search: bool = Falseby default) appends a web-search tool when the client supports it:GeminiChatClientimplementsSupportsWebSearchTool, so a nativegoogle_searchbuilt-in is added and passed through_prepare_toolsunchanged, alongside the function declarations. On the Gemini Developer API, mixing a built-in tool with function declarations requirestool_config.include_server_side_tool_invocations=True; the client's_prepare_config/_prepare_tool_confignever set it, so the API returns 400. (Ollama's client is notSupportsWebSearchTool, so no built-in is added — hence the crash is Gemini-only.)Platform nuance (confirmed by peer reports, §6): Vertex AI allows the combination without the flag and rejects the field as unknown, so the flag must be set only on the Developer-API path (the client already tracks this as
self._vertexai).5. Proposed upstream fix
Both fixes live in
agent-framework-gemini; neither requires changes to the harness or other providers.Declare tools with
parameters_json_schemainstead ofparameters.google-genai(≥ the pinned 1.75.0) exposesFunctionDeclaration.parameters_json_schema, forwarded to the API asparametersJsonSchema, which the model interprets as full JSON Schema ($ref/$defs,oneOf/anyOf, nullable unions). This offloads the whole compatibility surface to the API rather than lossily transcribing intotypes.Schemaby hand. Exactly one ofparameters/parameters_json_schemamay be set. (Google's own guidance for complex schemas is to useparametersJsonSchema.) A stricter alternative — flatten$ref/$defsand maponeOf→anyOfintotypes.Schema— is more code and still lossy for constructs the subset can't express.Enable
include_server_side_tool_invocationswhen a built-in tool is present. In_prepare_config(after buildingtools), when any entry is a nativetypes.Tooland the client is not in Vertex mode (self._vertexai), settool_config.include_server_side_tool_invocations = True(creating theToolConfigif absent). This is Google's prescribed fix and preserves web-search grounding (a documented, intended capability — the framework's own surfaces renderweb_search/search_tool_resultcontent).Optional hardening:
_prepare_toolscould dedupe identical native built-ins (the harness path can present twogoogle_searchtools); the API tolerates the duplicate with the flag on, so this is cosmetic.6. Prior art / duplicate check (2026-07-07)
No existing
microsoft/agent-frameworkissue reports either defect. Adjacent MAF issues found (different failure modes): #3434 (Python, Gemini streaming text lost with usage — closed), #3390 (.NET, Gemini image upload 400), #5490 (.NET tool calling), #6467 (.NET structured-output vs tool calls).The underlying Gemini-API behaviors are corroborated across the ecosystem, which both confirms the diagnosis and points at the accepted fixes:
$defs/$ref; fix is schema flattening or JSON-Schema passthrough — same as defect 1$defs/$ref— the constraint is API-side, not framework-specific$refs unsupported by Gemini — motivates the cycle fallback in any flatten-based fix$ref/$defs(400 INVALID_ARGUMENT) — same error class as defect 1include_server_side_tool_invocations=true; Vertex AI works without it and errors if set — directly validates defect 2's fix and the Vertex gating7. Verification
ValidationErroronparameters=; the identical schema onparameters_json_schema=builds cleanly and is accepted by the live API (generate_content_streamstreams) for both the$ref/$defsandoneOfshapes.gemini-3.5-flashon the Developer API (also with a duplicatedgoogle_searchtool).gemini-3.5-flashover AG-UI/CopilotKit completed a full turn —todos_addand other tools executed, an artifact was saved, and the plan streamed — with zero streaming errors and clean telemetry (0ValidationError, 0 400s). Full app test suite: 128 passed, including 10 new tests covering both crash shapes and both fixes.8. Suggested issue framing
$ref/$defs/oneOf(built-intodos_addcrashes every harness agent)" — defect 1; recommendparameters_json_schema.include_server_side_tool_invocations)" — defect 2; note the Developer-API-vs-Vertex gating.