when schema has an optional property, generateObject can't work
Description
const main = async () => {
const result = await generateObject({
model: openrouter("openai/gpt-4.1-mini"),
prompt: `Generate a random person`,
schema: z.object({
name: z.string().describe("The name of the person"),
age: z.number().describe("The age of the person"),
gender: z.string().describe("The gender of the person").optional()
}),
});
console.log(result.object);
};
get error
node:internal/process/promises:394
triggerUncaughtException(err, true /* fromPromise */);
^
APICallError [AI_APICallError]: Provider returned error
at <anonymous> (/Users/wangchengfeng/code/tts/node_modules/@openrouter/ai-sdk-provider/node_modules/.pnpm/@[email protected][email protected]/node_modules/@ai-sdk/provider-utils/src/response-handler.ts:56:16)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async postToApi (/Users/wangchengfeng/code/tts/node_modules/@openrouter/ai-sdk-provider/node_modules/.pnpm/@[email protected][email protected]/node_modules/@ai-sdk/provider-utils/src/post-to-api.ts:112:28)
at async OpenRouterChatLanguageModel.doGenerate (/Users/wangchengfeng/code/tts/node_modules/@openrouter/ai-sdk-provider/src/chat/index.ts:212:50)
at async fn (/Users/wangchengfeng/code/tts/node_modules/ai/src/generate-object/generate-object.ts:337:30)
at async <anonymous> (/Users/wangchengfeng/code/tts/node_modules/ai/src/telemetry/record-span.ts:18:22)
at async _retryWithExponentialBackoff (/Users/wangchengfeng/code/tts/node_modules/ai/src/util/retry-with-exponential-backoff.ts:96:12)
at async fn (/Users/wangchengfeng/code/tts/node_modules/ai/src/generate-object/generate-object.ts:308:32)
at async <anonymous> (/Users/wangchengfeng/code/tts/node_modules/ai/src/telemetry/record-span.ts:18:22)
at async generateObject (/Users/wangchengfeng/code/tts/node_modules/ai/src/generate-object/generate-object.ts:264:12) {
cause: undefined,
url: 'https://openrouter.ai/api/v1/chat/completions',
requestBodyValues: {
model: 'openai/gpt-4.1-mini',
models: undefined,
logit_bias: undefined,
logprobs: undefined,
top_logprobs: undefined,
user: undefined,
parallel_tool_calls: undefined,
max_tokens: undefined,
temperature: undefined,
top_p: undefined,
frequency_penalty: undefined,
presence_penalty: undefined,
seed: undefined,
stop: undefined,
response_format: {
type: 'json_schema',
json_schema: {
schema: {
type: 'object',
properties: { name: [Object], age: [Object], gender: [Object] },
required: [ 'name', 'age' ],
additionalProperties: false,
'$schema': 'http://json-schema.org/draft-07/schema#'
},
strict: true,
name: 'response'
}
},
top_k: undefined,
messages: [ { role: 'user', content: 'Generate a random person' } ],
include_reasoning: undefined,
reasoning: undefined,
usage: undefined,
plugins: undefined,
web_search_options: undefined,
provider: undefined
},
statusCode: 400,
responseHeaders: {
'access-control-allow-origin': '*',
'cf-ray': '96c4848c8ea98448-TPE',
connection: 'keep-alive',
'content-type': 'application/json',
date: 'Sat, 09 Aug 2025 04:30:22 GMT',
'permissions-policy': 'payment=(self "https://checkout.stripe.com" "https://connect-js.stripe.com" "https://js.stripe.com" "https://*.js.stripe.com" "https://hooks.stripe.com")',
'referrer-policy': 'no-referrer, strict-origin-when-cross-origin',
server: 'cloudflare',
'transfer-encoding': 'chunked',
vary: 'Accept-Encoding',
'x-content-type-options': 'nosniff'
},
responseBody: `{"error":{"message":"Provider returned error","code":400,"metadata":{"raw":"{\\n \\"error\\": {\\n \\"message\\": \\"Invalid schema for response_format 'response': In context=(), 'required' is required to be supplied and to be an array including every key in properties. Missing 'gender'.\\",\\n \\"type\\": \\"invalid_request_error\\",\\n \\"param\\": \\"response_format\\",\\n \\"code\\": null\\n }\\n}","provider_name":"OpenAI"}},"user_id":"user_2doK0G50fjDr4uxhzclhhBKxSpe"}`,
isRetryable: false,
data: {
error: {
code: 400,
message: 'Provider returned error',
type: null,
param: null
}
},
[Symbol(vercel.ai.error)]: true,
[Symbol(vercel.ai.error.AI_APICallError)]: true
}
If remove the optional property, everything is ok.
AI SDK Version
"ai": "^5.0.8", "@openrouter/ai-sdk-provider": "^1.1.0",
Ignore my attempt to fix this with Copilot (though it was insightful for me). This should be fixed with a better error message/handling.
The gist is: generateObject(), which uses structured outputs, requires all field to be present/required, so that request is invalid and fails correctly.
There is the possibility to emulate optional fields (kind of) with union types, apparently, see here -> https://platform.openai.com/docs/guides/structured-outputs#all-fields-must-be-required
Ignore my attempt to fix this with Copilot (though it was insightful for me). This should be fixed with a better error message/handling.
The gist is:
generateObject(), which uses structured outputs, requires all field to be present/required, so that request is invalid and fails correctly.There is the possibility to emulate optional fields (kind of) with union types, apparently, see here -> https://platform.openai.com/docs/guides/structured-outputs#all-fields-must-be-required
After reading the OpenAI documentation, I realized my use case was incorrectly.
Closing as duplicate of #128 - same root cause (generateObject with OpenAI models requires 'json' in messages). Tracking in #128.