Skip to content

fix(opencode): move type-specific keywords into gemini union branches - #49392

Open
axiom-of-choice wants to merge 1 commit into
anomalyco:devfrom
axiom-of-choice:fix/gemini-nullable-union-keywords
Open

axiom-of-choice wants to merge 1 commit into
anomalyco:devfrom
axiom-of-choice:fix/gemini-nullable-union-keywords

Conversation

@axiom-of-choice

@axiom-of-choice axiom-of-choice commented Sep 16, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #34130
Closes #48073

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

The Gemini sanitizer splits type: ["array","null"] into anyOf: [{ type: "array" }] but leaves items on the parent, and nullable objects leave properties/required there too. Gemini rejects both. This moves those keywords into the matching anyOf branch.

#43495 moves items only, so nullable objects still fail.

How did you verify your code works?

To reproduce, add the Kaggle MCP server and run any prompt on a Gemini model:

{ "mcp": { "kaggle": { "type": "remote", "url": "https://www.kaggle.com/mcp" } } }
opencode run -m google/gemini-3.8-flash "Say ok."

Before: 400 with 130 schema errors (full log in the comments). With this change, the same 71 tools through ProviderTransform.schema and @ai-sdk/google get a 200. Added a test with that schema shape.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Related PR Found:

PR #43495: fix(opencode): fold schema items into gemini union branches
#43495

Why it's related: This PR addresses a partial fix to the same issue. According to your PR description, #43495 moves items only, but nullable objects still fail. Your current PR (49392) extends the fix to handle both items for arrays AND properties/required for nullable objects in the anyOf branches. The two PRs are closely related, with your PR completing what #43495 started.

@axiom-of-choice

Copy link
Copy Markdown
Author

Full error from opencode run -m google/gemini-3.8-flash "Say ok." on 1.18.18 with only the Kaggle MCP enabled (130 lines):

error
Error: * GenerateContentRequest.tools[0].function_declarations[8].parameters.properties[request].properties[publishTime].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[31].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[31].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[31].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[32].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[32].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[32].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[41].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[41].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[41].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[42].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[42].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[42].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[43].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[43].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[43].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[50].parameters.properties[request].properties[filterCategoryIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[50].parameters.properties[request].properties[filterCategoryIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[50].parameters.properties[request].properties[filterCategoryIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[50].parameters.properties[request].properties[filterCategoryIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[hackathonTrackIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[hackathonTrackIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[readMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[readMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[readMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[hackathonTrackIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[53].parameters.properties[request].properties[hackathonTrackIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[competitionDataSourcesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[competitionDataSourcesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[modelDataSourcesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[modelDataSourcesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[kernelDataSourcesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[kernelDataSourcesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[datasetDataSourcesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[datasetDataSourcesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[categoryIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[categoryIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[competitionDataSources].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[competitionDataSources].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[datasetDataSources].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[datasetDataSources].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[categoryIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[categoryIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[modelDataSources].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[modelDataSources].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[kernelDataSources].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[63].parameters.properties[request].properties[kernelDataSources].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[kernelFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[documentTypes].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[documentTypes].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[countGroupings].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userLocations].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userLocations].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[occupationOrganizationNamesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[occupationOrganizationNamesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userLocationsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userLocationsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[grandmasterLevel].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[userIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[occupationOrganizationNames].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[userFilters].properties[occupationOrganizationNames].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[documentTypesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[documentTypesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[sharedViaGroupsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[sharedViaGroupsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[modelFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[modelFilters].properties[size].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[datasetFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[writeUpTypesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[writeUpTypesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[hackathonTrackIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[hackathonTrackIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[writeUpTypes].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[writeUpTypes].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[hackathonTrackIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[discussionFilters].properties[hackathonTrackIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[tagIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[tagIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[competitionFilters].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[competitionIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[competitionIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[competitionIdsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[competitionIdsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[tagIds].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[tagIds].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[sharedViaGroups].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[66].parameters.properties[request].properties[filters].properties[sharedViaGroups].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[keywordsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[keywordsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[collaborators].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[collaborators].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].items.properties[columns].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].items.properties[columns].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].items.properties[columnsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[dataSetter].items.properties[columnsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[image].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[image].properties[cropRectanglesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[image].properties[cropRectanglesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[image].properties[cropRectangles].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[image].properties[cropRectangles].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[data].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[data].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[licenses].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[licenses].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[collaboratorsSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[collaboratorsSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[licensesSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[licensesSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[keywords].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[71].parameters.properties[request].properties[settings].properties[keywords].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[72].parameters.properties[request].properties[publishTime].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[72].parameters.properties[request].properties[updateMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[72].parameters.properties[request].properties[updateMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[72].parameters.properties[request].properties[updateMask].properties[paths].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[trainingDataSetter].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[trainingDataSetter].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[trainingData].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[trainingData].any_of[0].items: missing field.
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[updateMask].properties: only allowed for OBJECT type
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[updateMask].properties[paths].items: field predicate failed: $type == Type.ARRAY
* GenerateContentRequest.tools[0].function_declarations[73].parameters.properties[request].properties[updateMask].properties[paths].any_of[0].items: missing field.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant