Skip to content

AgentEvaluator crashes with ValidationError when evaluating conversation_scenario eval cases #5214

Closed

Description

@ASRagab

Bug

AgentEvaluator.evaluate() raises a pydantic ValidationError when processing eval cases that use conversation_scenario (LLM-backed user simulation) instead of explicit conversation arrays.

Error

pydantic_core._pydantic_core.ValidationError: 1 validation error for _EvalMetricResultWithInvocation
expected_invocation
  Input should be a valid dictionary or instance of Invocation [type=model_type, input_value=None, input_type=NoneType]

at agent_evaluator.py:639

Root Cause

Type mismatch between the public and private invocation result models:

  • EvalMetricResultPerInvocation in eval_metrics.py:323 correctly declares:

    expected_invocation: Optional[Invocation] = Field(default=None, ...)
  • _EvalMetricResultWithInvocation in agent_evaluator.py:93 incorrectly declares:

    expected_invocation: Invocation  # required, non-optional

local_eval_service.py:285-287 intentionally sets expected_invocation=None when eval_case.conversation is None (i.e., when using conversation_scenario):

EvalMetricResultPerInvocation(
    actual_invocation=actual,
    expected_invocation=eval_case.conversation[idx]
    if eval_case.conversation
    else None,  # <-- None for conversation_scenario cases
)

This None flows through _get_eval_metric_results_with_invocation() at line 636 and into _EvalMetricResultWithInvocation() at line 639, which rejects it.

Fix

Two changes needed in agent_evaluator.py:

1. Make the field optional (line 93):

expected_invocation: Optional[Invocation] = None

2. Guard the downstream attribute accesses (lines 439-449 in _print_details):

"prompt": AgentEvaluator._convert_content_to_text(
    per_invocation_result.expected_invocation.user_content
    if per_invocation_result.expected_invocation else None
),
"expected_response": AgentEvaluator._convert_content_to_text(
    per_invocation_result.expected_invocation.final_response
    if per_invocation_result.expected_invocation else None
),
...
"expected_tool_calls": AgentEvaluator._convert_tool_calls_to_text(
    per_invocation_result.expected_invocation.intermediate_data
    if per_invocation_result.expected_invocation else None
),

Both _convert_content_to_text and _convert_tool_calls_to_text already accept Optional parameters and handle None gracefully.

Reproduction

# evalset with conversation_scenario (no explicit conversation array)
{
  "eval_set_id": "test",
  "eval_cases": [{
    "eval_id": "scenario_1",
    "conversation_scenario": {
      "starting_prompt": "Hello",
      "conversation_plan": "Ask the agent a question and accept the answer."
    },
    "session_input": {"app_name": "my_agent", "user_id": "user1", "state": {}}
  }]
}
# test_eval.py
import pytest
from google.adk.evaluation import AgentEvaluator

@pytest.mark.asyncio
async def test_scenario():
    await AgentEvaluator.evaluate(
        "my_agent",
        "path/to/evalset.json",
        num_runs=1,
    )

Crashes during post-processing in _get_eval_metric_results_with_invocation after all metrics have been computed.

Environment

  • google-adk[eval]>=1.28.0 (also confirmed on main branch — same code)
  • Python 3.11

Activity

  1. added
    eval[Component] This issue is related to evaluation
    on Apr 8, 2026
  2. added theissue type on Apr 8, 2026
  3. added a commit that references this issue on Apr 8, 2026
    5b1edaa
  4. added 2 commits that reference this issue on Apr 9, 2026
    e757949
    6668a79
  5. surajksharma07 commented on Apr 9, 2026

    @surajksharma07
    Collaborator

    @ASRagab _EvalMetricResultWithInvocation.expected_invocation (line 93) needs to be Optional[Invocation] = None, and _print_details (lines 439–449) also needs None guards on all three expected_invocation attribute accesses.

    The good news is that both _convert_content_to_text and _convert_tool_calls_to_text already handle None cleanly, so those should be straightforward one-liners.

    Could you check whether PR #5215 covers both spots and run it through a few scenario-based eval cases before we push it forward?
    Just want to make sure it holds up end-to-end before merging.

    Team is looking more into it.

  6. ASRagab commented on Apr 9, 2026

    @ASRagab
    ContributorAuthor

    @ASRagab _EvalMetricResultWithInvocation.expected_invocation (line 93) needs to be Optional[Invocation] = None, and _print_details (lines 439–449) also needs None guards on all three expected_invocation attribute accesses.

    The good news is that both _convert_content_to_text and _convert_tool_calls_to_text already handle None cleanly, so those should be straightforward one-liners.

    Could you check whether PR #5215 covers both spots and run it through a few scenario-based eval cases before we push it forward? Just want to make sure it holds up end-to-end before merging.

    Team is looking more into it.

    #5215 (comment)

  7. surajksharma07 commented on Apr 10, 2026

    @surajksharma07
    Collaborator

    That's exactly the kind of evidence we needed @ASRagab — before/after output with all three guard paths verified and a regression check is thorough testing done right!

    We currently have two PRs open for the same fix (#5215 and #5221), so the team will review both and consolidate before merging.

    Really appreciate you going the extra mile on the testing — we'll get this closed out soon!

    Team is looking more into it.

  8. surajksharma07 commented on Apr 10, 2026

    @surajksharma07
    Collaborator

    @ankursharmas Please have a look into it once.

  9. added a commit that references this issue on Apr 14, 2026
    61671df
  10. added a commit that references this issue on Apr 30, 2026
    8788d1c
  11. added a commit that references this issue on Aug 11, 2026
    2addd01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

eval[Component] This issue is related to evaluationneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions