Environment
genkit: 1.39.0
@genkit-ai/core: 1.39.0
@genkit-ai/google-genai: 1.39.0 (Vertex AI / vertexai plugin)
zod: 3.25.76
zod-to-json-schema: 3.25.2
- Node:
v22.20.0
- Model: a Gemini model on Vertex (e.g.
gemini-2.5-pro)
Summary
When a tool's inputSchema contains a property defined as a union (z.union([...]), which zod-to-json-schema emits as JSON Schema anyOf), the @genkit-ai/google-genai Vertex converter silently drops that property from the generated FunctionDeclaration.parameters.properties, but leaves the property name in the parent's required array.
Vertex then rejects the request:
GenkitError: INVALID_ARGUMENT: Error fetching from
https://aiplatform.googleapis.com/v1beta1/.../models/<model>:streamGenerateContent
[400 Bad Request] Unable to submit request because required fields ['tags'] are not defined in the schema properties.
Reproduction
import { z } from 'zod';
import { toJsonSchema } from '@genkit-ai/core/schema';
import { toGeminiTool } from '@genkit-ai/google-genai/lib/common/converters.mjs';
// Any tool with a union-typed property reproduces it.
const inputSchema = z.object({
id: z.string(),
tags: z.union([z.string(), z.array(z.string())]), // <-- union property
});
const decl = toGeminiTool({
name: 'updateItem',
description: 'Update an item by id.',
inputSchema: toJsonSchema({ schema: inputSchema }),
});
console.log(JSON.stringify(decl, null, 2));
Actual output (broken)
tags is absent from properties yet present in required — exactly the shape Vertex rejects with required fields ['tags'] are not defined in the schema properties.
Using the same tool through a model that consumes JSON Schema directly does not fail, because the union (anyOf) is preserved instead of being rewritten/dropped.
Root cause
In @genkit-ai/google-genai/src/common/converters.ts, toGeminiSchemaProperty:
function toGeminiSchemaProperty(property?: ToolDefinition['inputSchema']) {
if (!property || !property.type) {
return undefined; // (1) an `anyOf` node has no `.type`, so it is dropped
}
...
if (propertyType === 'object') {
const nestedProperties = {};
if (property.properties) {
Object.keys(property.properties).forEach((key) => {
nestedProperties[key] = toGeminiSchemaProperty(property.properties[key]);
// (2) a union/`anyOf` child becomes `undefined` -> removed on serialization
});
}
return {
...baseSchema,
type: SchemaType.OBJECT,
properties: nestedProperties,
required: property.required, // (3) `required` copied verbatim, still lists the dropped key
};
}
...
}
- The function early-returns
undefined for any node without a top-level .type. A union compiles to an anyOf node, which has no .type.
- In the
object branch, each child property is mapped through toGeminiSchemaProperty, so the union-typed child becomes undefined and disappears from properties once serialized.
required is copied straight from the source schema, so the now-missing property is still listed as required.
There is no handling for anyOf / oneOf / allOf anywhere in the converter — it branches only on .type (object, array, else primitive). Union/polymorphic properties are therefore never forwarded to Vertex, and the resulting properties/required mismatch makes Vertex reject the whole request.
Expected behaviour
One of:
- Support
anyOf in toGeminiSchemaProperty (preferred). Vertex's Schema type supports anyOf, so the converter should translate union nodes into a Vertex anyOf (recursively converting each member) instead of dropping them.
- At minimum, keep
required and properties consistent: if a property is dropped during conversion, it must also be removed from the parent's required array so a malformed declaration is never sent - ideally with a warning rather than silently producing invalid output.
Impact
Any tool whose input schema uses z.union (string-or-array values, polymorphic params, discriminated unions, etc.) cannot be used with the Vertex AI plugin — the request fails before reaching the model. The only workaround today is to avoid unions entirely in model-facing tool schemas and normalize the looser shape inside the tool handler instead.
Environment
genkit:1.39.0@genkit-ai/core:1.39.0@genkit-ai/google-genai:1.39.0(Vertex AI /vertexaiplugin)zod:3.25.76zod-to-json-schema:3.25.2v22.20.0gemini-2.5-pro)Summary
When a tool's
inputSchemacontains a property defined as a union (z.union([...]), whichzod-to-json-schemaemits as JSON SchemaanyOf), the@genkit-ai/google-genaiVertex converter silently drops that property from the generatedFunctionDeclaration.parameters.properties, but leaves the property name in the parent'srequiredarray.Vertex then rejects the request:
Reproduction
Actual output (broken)
{ "name": "updateItem", "description": "Update an item by id.", "parameters": { "type": "OBJECT", "properties": { "id": { "type": "STRING" } // "tags" is MISSING here ... }, "required": ["id", "tags"] // ... but still listed as required } }tagsis absent frompropertiesyet present inrequired— exactly the shape Vertex rejects withrequired fields ['tags'] are not defined in the schema properties.Using the same tool through a model that consumes JSON Schema directly does not fail, because the union (
anyOf) is preserved instead of being rewritten/dropped.Root cause
In
@genkit-ai/google-genai/src/common/converters.ts,toGeminiSchemaProperty:undefinedfor any node without a top-level.type. A union compiles to ananyOfnode, which has no.type.objectbranch, each child property is mapped throughtoGeminiSchemaProperty, so the union-typed child becomesundefinedand disappears frompropertiesonce serialized.requiredis copied straight from the source schema, so the now-missing property is still listed as required.There is no handling for
anyOf/oneOf/allOfanywhere in the converter — it branches only on.type(object,array, else primitive). Union/polymorphic properties are therefore never forwarded to Vertex, and the resultingproperties/requiredmismatch makes Vertex reject the whole request.Expected behaviour
One of:
anyOfintoGeminiSchemaProperty(preferred). Vertex'sSchematype supportsanyOf, so the converter should translate union nodes into a VertexanyOf(recursively converting each member) instead of dropping them.requiredandpropertiesconsistent: if a property is dropped during conversion, it must also be removed from the parent'srequiredarray so a malformed declaration is never sent - ideally with a warning rather than silently producing invalid output.Impact
Any tool whose input schema uses
z.union(string-or-array values, polymorphic params, discriminated unions, etc.) cannot be used with the Vertex AI plugin — the request fails before reaching the model. The only workaround today is to avoid unions entirely in model-facing tool schemas and normalize the looser shape inside the tool handler instead.