diff --git a/internal/util/gemini_schema.go b/internal/util/gemini_schema.go index bd4dae893..8c97b72cc 100644 --- a/internal/util/gemini_schema.go +++ b/internal/util/gemini_schema.go @@ -1322,6 +1322,7 @@ func removeUnsupportedKeywords(jsonStr string, options jsonSchemaCleanOptions) s "$schema", "$defs", "definitions", "const", "$ref", "$id", "id", "additionalProperties", "$anchor", "$vocabulary", "$dynamicRef", "$dynamicAnchor", "propertyNames", "patternProperties", // Gemini doesn't support these schema keywords + "dependentRequired", "dependentSchemas", "dependencies", "if", "then", "else", "$comment", "enumDescriptions", "enumTitles", "prefill", "deprecated", "encrypted", // Schema metadata fields unsupported by Gemini "additionalItems", "unevaluatedProperties", "unevaluatedItems", "contentSchema", diff --git a/internal/util/gemini_schema_test.go b/internal/util/gemini_schema_test.go index f31410813..5da8f3f4d 100644 --- a/internal/util/gemini_schema_test.go +++ b/internal/util/gemini_schema_test.go @@ -3039,3 +3039,99 @@ func TestCleanJSONSchema_EnforcesObjectTypeForProperties_Issue6394(t *testing.T) } }) } + +// TestCleanJSONSchema_RemovesDependentKeywords_Issue6539 covers Issue #6539: +// JSON Schema keywords dependentRequired, dependentSchemas, and dependencies are rejected by +// the Gemini/Antigravity backend with 400 INVALID_ARGUMENT ("Unknown name \"dependentRequired\" ... Cannot find field") +// and must be stripped while preserving legitimate tool properties with those names. +func TestCleanJSONSchema_RemovesDependentKeywords_Issue6539(t *testing.T) { + input := `{ + "type": "object", + "properties": { + "baseline_url": {"type": "string"}, + "candidate_url": {"type": "string"}, + "options": { + "type": "object", + "properties": { + "threshold": {"type": "number"}, + "diff_color": {"type": "string"} + }, + "dependentRequired": { + "threshold": ["diff_color"] + }, + "dependentSchemas": { + "threshold": { + "properties": { + "diff_color": {"type": "string"} + } + } + }, + "dependencies": { + "threshold": ["diff_color"] + } + }, + "dependentRequired": { + "type": "string", + "description": "Legitimate property named dependentRequired" + }, + "dependentSchemas": { + "type": "object", + "properties": { + "nested": {"type": "string"} + }, + "dependentRequired": { + "nested": ["other"] + } + }, + "dependencies": { + "type": "string", + "description": "Legitimate property named dependencies" + } + }, + "dependentRequired": { + "baseline_url": ["candidate_url"] + }, + "dependentSchemas": { + "baseline_url": { + "required": ["candidate_url"] + } + }, + "dependencies": { + "baseline_url": ["candidate_url"] + } + }` + + cleaners := map[string]func(string) string{ + "Antigravity": CleanJSONSchemaForAntigravity, + "AntigravityTool": func(s string) string { return CleanJSONSchemaForAntigravityTool(s, false) }, + "AntigravityResponse": CleanJSONSchemaForAntigravityResponse, + "Gemini": CleanJSONSchemaForGemini, + "GeminiJSONSchema": CleanJSONSchemaForGeminiJSONSchema, + } + + for name, clean := range cleaners { + t.Run(name, func(t *testing.T) { + got := clean(input) + parsed := gjson.Parse(got) + + for _, kw := range []string{"dependentRequired", "dependentSchemas", "dependencies"} { + if parsed.Get(kw).Exists() { + t.Errorf("[%s] root %q was not removed: %s", name, kw, got) + } + if parsed.Get("properties.options." + kw).Exists() { + t.Errorf("[%s] nested properties.options.%s was not removed: %s", name, kw, got) + } + } + + if parsed.Get("properties.dependentSchemas.dependentRequired").Exists() { + t.Errorf("[%s] properties.dependentSchemas.dependentRequired was not removed: %s", name, got) + } + + for _, prop := range []string{"dependentRequired", "dependentSchemas", "dependencies"} { + if !parsed.Get("properties." + prop).Exists() { + t.Errorf("[%s] legitimate property %q was removed: %s", name, prop, got) + } + } + }) + } +}