Skip to content

Retrieval tools declare query as optional, and a call without it fails the run聽#7677

Description

@roydonsequeira

馃敶 Required Information

Describe the Bug:
BaseRetrievalTool._get_declaration (src/google/adk/tools/retrieval/base_retrieval_tool.py) declares one parameter, query, but does not list it in required, in either the JSON schema or the types.Schema form. So every tool built on it tells the model the query is optional: LlamaIndexRetrieval and FilesRetrieval with any model, and VertexAiRagRetrieval with non-Gemini models. None of them can run without one. LlamaIndexRetrieval reads args['query'] and raises KeyError, VertexAiRagRetrieval raises ValueError, and the exception fails the run.

load_memory has the same single query parameter and declares it as required in both forms, and a FunctionTool declaration lists every parameter that has no default and is not Optional.

Steps to Reproduce:

  1. Install google-adk 2.12.0 with llama-index-core (also reproduced on main at facd7dda).
  2. Run the script under "Minimal Reproduction Code". It needs no network or API key: it prints the declared requirements, then lets a scripted model call the tool with no arguments, which the declaration allows.

Expected Behavior:
The declaration lists query in required, as load_memory's does, so the model knows it has to send one.

Observed Behavior:

required: search_docs=None, load_memory=['query']
run failed: KeyError: 'query'

Environment Details:

  • ADK Library Version (pip show google-adk): 2.12.0, main at facd7dda
  • Desktop OS: Windows 11
  • Python Version (python -V): Python 3.11.3

Model Information:

  • Are you using LiteLLM: No, though LiteLlm sends the same parameters (see "Logs")
  • Which model is being used: N/A, a scripted model makes the call

馃煛 Optional Information

Regression:
No. The declaration has not listed query as required since the tool was added.

Logs:
The parameters a model receives for a LlamaIndexRetrieval tool, recorded through Runner, and the same declaration converted by LiteLlm:

Gemini request:  {'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'The query to retrieve.'}}}
LiteLlm request: {'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'The query to retrieve.'}}}

Where the run fails:

  File ".../google/adk/tools/retrieval/llama_index_retrieval.py", line 41, in run_async
    nodes = self.retriever.retrieve(args['query'])
KeyError: 'query'

Screenshots / Video:
N/A

Additional Context:
The Gemini API's VALIDATED function calling mode "ensures function schema adherence", but a call with no arguments adheres to this schema. Listing query in required in both forms, as load_memory_tool.py does, fixes it.

Minimal Reproduction Code:

import asyncio

from google.adk.agents import Agent
from google.adk.models.base_llm import BaseLlm
from google.adk.models.llm_response import LlmResponse
from google.adk.runners import InMemoryRunner
from google.adk.tools.load_memory_tool import load_memory_tool
from google.adk.tools.retrieval.llama_index_retrieval import LlamaIndexRetrieval
from google.genai import types
from llama_index.core import Document
from llama_index.core import VectorStoreIndex
from llama_index.core.embeddings import MockEmbedding


class ScriptedModel(BaseLlm):
  """Calls the tool with no arguments, which its declaration allows."""

  model: str = "scripted"

  async def generate_content_async(self, llm_request, stream=False):
    yield LlmResponse(
        content=types.Content(
            role="model",
            parts=[types.Part.from_function_call(name="search_docs", args={})],
        )
    )


async def main():
  index = VectorStoreIndex.from_documents(
      [Document(text="Retrieval tools take the query to look up.")],
      embed_model=MockEmbedding(embed_dim=2),
  )
  tool = LlamaIndexRetrieval(
      name="search_docs",
      description="Searches the product docs.",
      retriever=index.as_retriever(),
  )
  retrieval_required = tool._get_declaration().parameters_json_schema.get(
      "required"
  )
  memory_required = load_memory_tool._get_declaration().parameters_json_schema[
      "required"
  ]
  print(f"required: search_docs={retrieval_required}, load_memory={memory_required}")

  runner = InMemoryRunner(
      agent=Agent(name="assistant", model=ScriptedModel(), tools=[tool]),
      app_name="repro",
  )
  session = await runner.session_service.create_session(
      app_name="repro", user_id="u"
  )
  try:
    async for _ in runner.run_async(
        user_id="u",
        session_id=session.id,
        new_message=types.Content(
            role="user", parts=[types.Part(text="Look it up.")]
        ),
    ):
      pass
  except KeyError as e:
    print(f"run failed: KeyError: {e}")


asyncio.run(main())

How often has this issue occurred?:

  • Always (100%) when the model leaves the query out

Activity

  1. sanketpatil06 commented on Oct 23, 2026

    @sanketpatil06

    Hi @roydonsequeira, thanks for the detailed report and the reproduction script.

    We confirmed the issue on main: BaseRetrievalTool._get_declaration doesn't list query as required in either declaration form. Your PR #7678 adds required to both forms, along with tests. Our team is reviewing it now, and we'll post updates on the PR.

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

Metadata

Metadata

Assignees

Labels

tools[Component] This issue is related to tools

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions