Skip to content

Conversation

@MarlzRana
Copy link
Contributor

@MarlzRana MarlzRana commented Oct 25, 2025

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

Problem:
In 1.45.0 of the google-genai Python SDK release, changes were made to handle null types out of the box. This conflicts with the code in the google-adk to handle this, and causes the below exception:

Error in event_generator: 400 INVALID_ARGUMENT. {'error': {'code': 400, 'message': 'Unable to submit request because `create_payee` functionDeclaration `parameters.person.age` schema specified other fields alongside any_of. When using any_of, it must be the only field set. Learn more: https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling', 'status': 'INVALID_ARGUMENT'}}

This is what the Gemini schema looks like when they collide:

"age": {
          "additional_properties": null,
          "defs": null,
          "ref": null,
          "any_of": [
            {
              "additional_properties": null,
              "defs": null,
              "ref": null,
              "any_of": null,
              "default": null,
              "description": null,
              "enum": null,
              "example": null,
              "format": null,
              "items": null,
              "max_items": null,
              "max_length": null,
              "max_properties": null,
              "maximum": null,
              "min_items": null,
              "min_length": null,
              "min_properties": null,
              "minimum": null,
              "nullable": null,
              "pattern": null,
              "properties": null,
              "property_ordering": null,
              "required": null,
              "title": null,
              "type": "STRING"
            },
            {
              "additional_properties": null,
              "defs": null,
              "ref": null,
              "any_of": null,
              "default": null,
              "description": null,
              "enum": null,
              "example": null,
              "format": null,
              "items": null,
              "max_items": null,
              "max_length": null,
              "max_properties": null,
              "maximum": null,
              "min_items": null,
              "min_length": null,
              "min_properties": null,
              "minimum": null,
              "nullable": true,
              "pattern": null,
              "properties": null,
              "property_ordering": null,
              "required": null,
              "title": null,
              "type": "OBJECT"
            }
          ],
          "default": null,
          "description": "Person's age",
          "enum": null,
          "example": null,
          "format": null,
          "items": null,
          "max_items": null,
          "max_length": null,
          "max_properties": null,
          "maximum": null,
          "min_items": null,
          "min_length": null,
          "min_properties": null,
          "minimum": null,
          "nullable": null,
          "pattern": null,
          "properties": null,
          "property_ordering": null,
          "required": null,
          "title": null,
          "type": "OBJECT"
        },

As one can observe, this schema does have fields specified alongside any_of and it is also incorrect logically:

  • the overall schema is not marked as nullable
  • we should not need to use any_of, to define an extra schema for null

Instead, we should be seeing the absence of any_of where we have a nullable type. Instead we should see the type specified for the type, and nullable set to true.

The line in the code that cause this is:

elif schema.get("type") == "null":
schema["type"] = ["object", "null"]

It doesn't correctly handle the situation were we define a nullable using anyOf (where one type is the non-nullable type, and the other is the null type), as it's recursion operates on the individual type property level - it does not consider a type value in the context of another, as one has with anyOf.

Solution:

Let's defer to the changes to handle null types properly (one of the mechanisms being flattening an any_of type pair with one element being null).

This results in the correct Gemini schema being produced:

"age": {
          "additional_properties": null,
          "defs": null,
          "ref": null,
          "any_of": null,
          "default": null,
          "description": "Person's age",
          "enum": null,
          "example": null,
          "format": null,
          "items": null,
          "max_items": null,
          "max_length": null,
          "max_properties": null,
          "maximum": null,
          "min_items": null,
          "min_length": null,
          "min_properties": null,
          "minimum": null,
          "nullable": true,
          "pattern": null,
          "properties": null,
          "property_ordering": null,
          "required": null,
          "title": null,
          "type": "STRING"
        },

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Manual End-to-End (E2E) Tests:

I utilized the ADK web, and no longer observed the error.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

This was quite a tricky issue to debug, but here are the notes I took:

image image image image

I hope this helps and I look forward to hearing back from you soon! 😄

@google-cla
Copy link

google-cla bot commented Oct 25, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist
Copy link

Summary of Changes

Hello @MarlzRana, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the schema sanitization process for Gemini, primarily focusing on how nullable types are interpreted and converted. It includes an update to the google-genai SDK and a significant refactor of the schema utility, removing a custom sanitization function in favor of a more robust approach that correctly handles complex type definitions, especially those involving multiple types or anyOf constructs with nullability. The changes aim to improve the accuracy and consistency of schema representation.

Highlights

  • Dependency Update: The google-genai SDK dependency has been updated from version >=1.41.0 to >=1.44.0 in pyproject.toml.
  • Schema Sanitization Refactor: The _sanitize_schema_type helper function, which previously handled schema type normalization, has been removed. The _sanitize_schema_formats_for_gemini function was updated to reflect this change and now defaults an empty schema to type 'object'.
  • Improved Nullable Type Handling: The logic for interpreting nullable types has been enhanced. Schemas defining multiple types, including 'null' (e.g., ['string', 'null', 'integer']), are now correctly represented using any_of for the non-null types and setting the nullable property to true.
  • Enhanced anyOf Support: New unit tests have been added to specifically validate the correct processing of anyOf schemas that include 'null' as one of the possible types, ensuring accurate conversion to Gemini's schema representation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot
Copy link
Collaborator

adk-bot commented Oct 25, 2025

Response from ADK Triaging Agent

Hello @MarlzRana, thank you for your contribution!

Before we can review this PR, please address the following items from our contribution guidelines:

  • Sign the Contributor License Agreement (CLA): It looks like the CLA check has failed. You'll need to sign the Google CLA before we can accept your contribution.
  • Link to an Issue: This PR contains a bug fix. Could you please create a GitHub issue that this PR addresses and link it in the PR description? If an issue already exists, please link it.
  • Provide a Testing Plan: Could you please fill out the "Testing Plan" section in the PR description to explain how you've tested these changes?

You can find more details in our contribution guide.

Thank you!

@adk-bot adk-bot added the tools [Component] This issue is related to tools label Oct 25, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the google-genai dependency and refactors the schema sanitization logic to leverage improvements in the new version. The removal of the custom _sanitize_schema_type function simplifies the codebase, and the updated logic now correctly handles nullable and multi-typed fields by using anyOf, which is a significant improvement over the previous implementation. The test suite has been updated accordingly with new tests for nullable anyOf cases and adjustments to existing tests to reflect the more accurate schema conversion. The changes are well-implemented and improve both correctness and maintainability.

@MarlzRana MarlzRana force-pushed the marlzrana/fix-nullable-types-w-gen-ai-bump branch from 11623cb to 73b1c6c Compare October 25, 2025 14:59
Jacksunwei added a commit that referenced this pull request Oct 26, 2025
This commit contains detailed analysis materials for investigating and
reviewing the nullable fields bug with Google GenAI bump.

Analysis Materials:
- issue_3281_analysis.md: Root cause analysis and detailed bug flow
- pr_3280_review.md: Comprehensive review of the proposed fix

Test Scripts (for reproduction and validation):
- test_nullable_issue.py: Basic reproduction of the Pydantic schema issue
- test_sanitize_issue.py: Demonstrates the problematic transformation
- test_exact_adk_flow.py: Traces the complete ADK processing flow
- test_anyof_unwrap.py: Shows google-genai can unwrap correctly
- test_unwrap_with_description.py: Tests unwrapping with metadata
- test_gemini_api_error.py: Analyzes the schema structure problem
- test_fix_proposal.py: Initial fix approach (tracking anyOf context)
- test_pr_3280_fix.py: Validates the PR #3280 approach
- test_pr_edge_cases.py: Comprehensive edge case testing

Key Findings:
- Root cause: _sanitize_schema_type transforms {"type": "null"} to
  {"type": ["object", "null"]} which breaks google-genai unwrapping
- PR #3280 correctly fixes this by removing _sanitize_schema_type
  entirely and deferring to google-genai>=1.45.0
- All edge cases are properly handled by the new approach
- The fix is minimal, clean, and improves type handling overall

Recommendation: APPROVE PR #3280

Related: #3281, PR #3280
@speedstorm1
Copy link
Member

@speedstorm1 will take a look at this

@Jacksunwei Jacksunwei added this to the 10/27 ADK Week milestone Oct 27, 2025
@xuanyang15 xuanyang15 assigned speedstorm1 and unassigned xuanyang15 Oct 27, 2025
copybara-service bot pushed a commit that referenced this pull request Oct 27, 2025
Merge #3280

Fixes: #3281
COPYBARA_INTEGRATE_REVIEW=#3280 from MarlzRana:marlzrana/fix-nullable-types-w-gen-ai-bump ad28b82
PiperOrigin-RevId: 824659954
@xuanyang15
Copy link
Collaborator

Thank you @speedstorm1, this PR is merged now.

@xuanyang15 xuanyang15 closed this Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tools [Component] This issue is related to tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nullable Fields Broken with Google GenAI Bump

5 participants