Skip to content

fix(agents-core): enum missing type field when not nullable#1015

Merged
seratch merged 1 commit intoopenai:mainfrom
alexian-ic:fix/missing-type-for-enum
Feb 25, 2026
Merged

fix(agents-core): enum missing type field when not nullable#1015
seratch merged 1 commit intoopenai:mainfrom
alexian-ic:fix/missing-type-for-enum

Conversation

@alexian-ic
Copy link
Copy Markdown
Contributor

Note: I am new(ish) to this package, so please let me know if I missed something! I followed the contributing guidelines and confirmed that the primary fix resolves the API error from Gemini that we were seeing in my local environment.

Overview

Primary issue: buildEnum in zodJsonSchemaCompat fallback omits type, breaking non-OpenAI providers

When a tool schema uses z.enum([...]).optional() without .nullable(), the primary schema path (zodResponsesFunction from the openai SDK) throws because it enforces the Structured Outputs requirement that .optional() must be paired with .nullable(). This is seemingly expected — @openai/agents-core catches the error and falls back to its own converter in zodJsonSchemaCompat.ts.

The problem is that the fallback's buildEnum function produces { enum: ["A", "B"] } without a type field. The primary path correctly produces { type: "string", enum: ["A", "B"] }, but the fallback does not.

The fallback logic is correct enough to work with OpenAI's API (which is lenient about the missing type), but providers that follow OpenAPI 3.0 conventions reject it:

GenerateContentRequest.tools[0].function_declarations[...].parameters.properties[...].enum:
  only allowed for STRING type

Since the .optional() without .nullable() pattern is common for tool parameter schemas (where the Structured Outputs constraint doesn't apply), the fallback is the main code path for many real-world tool schemas. This makes the missing type a practical issue for anyone using non-OpenAI providers via @openai/agents-extensions.

Secondary issue: This was noticed when building out the tests for the primary issue. Basically,buildEnum uses Object.values() for native enum objects, but this method includes the reverse-mapping keys for numeric enums

For a numeric enum like enum Priority { Low = 0, High = 1 }, TypeScript compiles this to { Low: 0, High: 1, "0": "Low", "1": "High" }. The original Object.values() call returns [0, 1, "Low", "High"] instead of just [0, 1]. This was a pre-existing issue, but adding type inference fix made it visible — the incorrect values would cause the type to be inferred as ["string", "number"] instead of "number".

Detailed fixes

In packages/agents-core/src/utils/zodJsonSchemaCompat.ts:

  1. Type inference — instead of returning { enum: values } at each branch, persist values and infer type at the end. The logic aligns with parseEnumDef and parseNativeEnumDef in the openai SDK's vendored zod-to-json-schema.

  2. Reverse-mapping filterObject.values() replaced with resolveNativeEnumValues() for object-valued branches, filtering out TypeScript's numeric reverse-mapping keys. Matches parseNativeEnumDef L12-L15.

Steps to reproduce the primary issue

import { tool } from '@openai/agents';
import { z } from 'zod';

const myTool = tool({
  name: 'my_tool',
  description: 'Example tool with enum',
  parameters: z.object({
    mode: z.enum(['FAST', 'SLOW']).optional().describe('Processing mode'),
  }),
  execute: async ({ mode }) => mode ?? 'FAST',
});

// Use with any non-OpenAI provider (e.g. @ai-sdk/google via @openai/agents-extensions)
// The tool schema sent to the provider will have:
//   { "mode": { "enum": ["FAST", "SLOW"] } }
// instead of:
//   { "mode": { "type": "string", "enum": ["FAST", "SLOW"] } }

Expected behavior

buildEnum should infer and include the type from the enum values (e.g. "string" for z.enum(), "number" for numeric z.nativeEnum()), matching what the primary path produces.

Actual behavior

buildEnum produces { enum: [...] } without type.

Updated tests

  • Existing enum tests now assert type: "string" (previously missing)
  • Added test for numeric z.nativeEnum() asserting type: "number"
  • Added test for mixed numeric/string z.nativeEnum() asserting type: ["string", "number"]

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: 57cc341

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@openai/agents-core Patch
@openai/agents-extensions Patch
@openai/agents-openai Patch
@openai/agents-realtime Patch
@openai/agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@seratch seratch added this to the 0.5.x milestone Feb 25, 2026
@seratch seratch merged commit 3da9364 into openai:main Feb 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants