fix: sanitize Gemini tool schema required fields (#64284) (thanks @xxxxxmax)

This commit is contained in:
Peter Steinberger
2026-04-10 14:53:35 +01:00
parent 0dbcf81b34
commit fe1fd055d5
3 changed files with 18 additions and 2 deletions

View File

@@ -105,6 +105,7 @@ Docs: https://docs.openclaw.ai
- Plugins: treat duplicate `registerService` calls from the same plugin id as idempotent so snapshot and activation loads no longer emit spurious `service already registered` diagnostics. (#62033, #64128) Thanks @ly85206559.
- Discord/TTS: route auto voice replies through the native voice-note path so Discord receives Opus voice messages instead of regular audio attachments. (#64096) Thanks @LiuHuaize.
- Config/plugins: use plugin-owned command alias metadata when `plugins.allow` contains runtime command names like `dreaming`, and point users at the owning plugin instead of stale plugin-not-found guidance. (#64242) Thanks @feiskyer.
- Agents/Gemini: strip orphaned `required` entries from Gemini tool schemas so provider validation no longer rejects tools after schema cleanup or union flattening. (#64284) Thanks @xxxxxmax.
## 2026.4.9

View File

@@ -68,12 +68,21 @@ describe("cleanSchemaForGemini", () => {
expect(cleaned.required).toBeUndefined();
});
it("leaves required as-is when properties is absent", () => {
it("removes required from object schemas when properties is absent", () => {
const cleaned = cleanSchemaForGemini({
type: "object",
required: ["a", "b"],
}) as { required?: string[] };
expect(cleaned.required).toBeUndefined();
});
it("leaves required as-is for non-object schemas when properties is absent", () => {
const cleaned = cleanSchemaForGemini({
type: "array",
required: ["a", "b"],
}) as { required?: string[] };
expect(cleaned.required).toEqual(["a", "b"]);
});

View File

@@ -214,12 +214,18 @@ function simplifyUnionVariants(params: { obj: Record<string, unknown>; variants:
// Gemini rejects object schemas whose `required` entries do not exist in `properties`.
function sanitizeRequiredFields(schema: Record<string, unknown>): Record<string, unknown> {
if (!Array.isArray(schema.required)) {
return schema;
}
if (
!Array.isArray(schema.required) ||
!schema.properties ||
typeof schema.properties !== "object" ||
Array.isArray(schema.properties)
) {
if (schema.type === "object") {
delete schema.required;
}
return schema;
}