diff --git a/src/_vendor/zod-to-json-schema/parsers/optional.ts b/src/_vendor/zod-to-json-schema/parsers/optional.ts index 9b3e9731f..aee9606e4 100644 --- a/src/_vendor/zod-to-json-schema/parsers/optional.ts +++ b/src/_vendor/zod-to-json-schema/parsers/optional.ts @@ -3,6 +3,15 @@ import { JsonSchema7Type, parseDef } from '../parseDef'; import { Refs } from '../Refs'; export const parseOptionalDef = (def: ZodOptionalDef, refs: Refs): JsonSchema7Type | undefined => { + if (refs.openaiStrictMode) { + const fieldName = refs.propertyPath?.slice(-1)[0] || 'unknown'; + console.warn( + `Warning: Field "${fieldName}" uses .optional() which is not supported by OpenAI API Structured Outputs. ` + + `Please use .nullable() instead. ` + + `See: https://platform.openai.com/docs/guides/structured-outputs#all-fields-must-be-required`, + ); + } + if (refs.currentPath.toString() === refs.propertyPath?.toString()) { return parseDef(def.innerType._def, refs); } diff --git a/tests/helpers/zod.test.ts b/tests/helpers/zod.test.ts index 493b4c0c8..e35a951ca 100644 --- a/tests/helpers/zod.test.ts +++ b/tests/helpers/zod.test.ts @@ -88,6 +88,24 @@ describe('zodResponseFormat', () => { `); }); + it('warns when using optional fields with OpenAI API', () => { + const consoleSpy = jest.spyOn(console, 'warn'); + + zodResponseFormat( + z.object({ + required: z.string(), + optional: z.string().optional(), + }), + 'test', + ); + + expect(consoleSpy).toHaveBeenCalledWith( + expect.stringContaining('uses .optional() which is not supported by OpenAI API Structured Outputs'), + ); + + consoleSpy.mockRestore(); + }); + it('automatically adds properties with defaults to `required`', () => { expect( zodResponseFormat(