Skip to content

Python: .NET: [Bug]: ContainsExpected reports missing expected output as a model failure #9019

Description

Summary

EvalChecks.ContainsExpected() returns a failed check when EvalItem.ExpectedOutput is null or empty, even though the check cannot be applied. This turns a missing test fixture into a failed agent evaluation and is inconsistent with the built-in expected-tool-call check, which treats absent expectations as non-failing.

Version and evidence

Reproduced from main at commit e9857bd75a21843803431a2e7dfa41ee9b017ac7 on 2026-10-03.

Package version: source build at the SHA above. The reproduction was compiled for net10.0 and run with .NET SDK 10.0.400; the repository pins 10.0.401.

Minimal reproduction

Proposed xUnit regression test in dotnet/tests/Microsoft.Agents.AI.UnitTests/EvaluationDefectRegressionTests.cs:

[Fact]
public void ContainsExpected_WithoutExpectedOutput_ThrowsConfigurationError()
{
    // Arrange
    EvalCheck check = EvalChecks.ContainsExpected();
    EvalItem item = new(query: "question", response: "answer");

    // Act & Assert: a missing fixture is a configuration problem, not a model failure.
    Assert.Throws<InvalidOperationException>(() => check(item));
}

Run with:

dotnet run --project dotnet/tests/Microsoft.Agents.AI.UnitTests/Microsoft.Agents.AI.UnitTests.csproj `
  -f net10.0 --no-restore -- `
  --filter-query "/*/Microsoft.Agents.AI.UnitTests/EvaluationDefectRegressionTests/ContainsExpected_WithoutExpectedOutput_ThrowsConfigurationError"

The test fails with Assert.Throws() Failure: No exception was thrown; the returned result has Passed == false and reason ExpectedOutput is not set; check cannot be applied.

Actual behavior

A missing fixture produces an ordinary failed check, and LocalEvaluator reports it as an agent evaluation failure (EvalChecks.cs:224-242, LocalEvaluator.cs:43-58). ToolCallArgsMatch uses the opposite missing-input policy and returns a passing check (EvalChecks.cs:138-146).

Expected behavior

Missing ExpectedOutput should be surfaced as test/evaluator configuration, not as evidence that the agent failed. The regression test expresses the recommended behavior: throw a clear InvalidOperationException before an evaluation metric is produced.

Impact

Any test suite that conditionally supplies expected outputs can produce false model failures when one fixture is omitted. CI output points maintainers toward the agent response even though the actionable defect is in the test data; this is especially confusing because the reason itself says that the check could not be applied (EvalChecks.cs:228-230).

Python parity

Python has no built-in contains_expected check. Function-evaluator injection converts missing expected_output to "" in python/packages/core/agent_framework/_evaluation.py:1253-1301, and its focused test asserts that conversion in python/packages/core/tests/core/test_local_eval.py:127-149. Python's expected-tool-call checks pass when no expectations are supplied in _evaluation.py:1141-1164 and _evaluation.py:1183-1208, so Python does not have the same built-in failure behavior.

Duplicate search

No open or closed issue or PR matched ContainsExpected. A broader ExpectedOutput "not set" search only surfaced unrelated sample PR #5823, which is not a duplicate.

Fix options

  1. Throw a configuration exception (recommended). Throw InvalidOperationException when ExpectedOutput is null or empty. This is an observable behavior change for invalid/missing fixtures, but it changes no signature and requires no PublicAPI.Shipped.txt or PublicAPI.Unshipped.txt update; ContainsExpected is already shipped in dotnet/src/Microsoft.Agents.AI/PublicAPI/net10.0/PublicAPI.Shipped.txt:946.
  2. Skip/pass when no expectation is supplied. Return a passing result, matching ToolCallArgsMatch. This is also a behavior change with no public API baseline change and is convenient for mixed datasets, but it can silently hide an accidentally missing fixture.
  3. Represent applicability separately. Add an applicability/configuration state to EvalCheckResult and teach LocalEvaluator how to aggregate it. This gives the most precise diagnostics, but EvalCheckResult is a shipped public record in PublicAPI.Shipped.txt:295-305; changing its primary constructor/deconstructor would be a public API break, while an additive property would require PublicAPI.Unshipped.txt entries and more aggregation design.

I recommend option 1 because it fails fast on the real problem without silently accepting incomplete fixtures or changing public API. I would be happy to submit a PR with the regression test and a documented missing-input policy for the built-in checks.

Activity

  1. added
    .NETUsage: [Issues, PRs], Target: .Net
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 3, 2026
  2. changed the title [-].NET: [Bug]: `ContainsExpected` reports missing expected output as a model failure[/-] [+]Python: .NET: [Bug]: `ContainsExpected` reports missing expected output as a model failure[/+] on Oct 3, 2026
  3. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Oct 3, 2026
  4. github-actions commented on Oct 3, 2026

    @github-actions
    Contributor

    🤖 Automated triage reproduction notes (agent-authored — trust but verify)

    Agent analysis

    The bug reproduces in dotnet/src/Microsoft.Agents.AI/Evaluation/EvalChecks.cs::ContainsExpected when EvalItem.ExpectedOutput is null or empty; it returns a failed result that LocalEvaluator maps to an unacceptable metric. Minimal repro: create an EvalItem without ExpectedOutput, invoke EvalChecks.ContainsExpected(), and assert that configuration is surfaced before metric production.

    • Failing test: dotnet/tests/Microsoft.Agents.AI.UnitTests/EvaluationDefectRegressionTests.cs
    • Files examined: dotnet/src/Microsoft.Agents.AI/Evaluation/EvalItem.cs, dotnet/src/Microsoft.Agents.AI/Evaluation/EvalChecks.cs, dotnet/src/Microsoft.Agents.AI/Evaluation/LocalEvaluator.cs, dotnet/tests/Microsoft.Agents.AI.UnitTests/EvaluationTests.cs
    • Tests run: EvaluationTests.ContainsExpected_FailsWhenNoExpectedOutput, EvaluationDefectRegressionTests.ContainsExpected_WithoutExpectedOutput_ThrowsConfigurationError
  5. added
    agentsUsage: [Issues, PRs], Target: Single agent
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 3, 2026
  6. joslat commented on Oct 3, 2026

    @joslat
    ContributorAuthor

    A draft implementation is available for an early look on the fix/eval-result-consistency branch, currently at commit f830367b9. No PR has been opened yet; I am waiting for maintainer feedback on the intended behavior before polishing and opening the combined PR.

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

Metadata

Metadata

Labels

.NETUsage: [Issues, PRs], Target: .NetagentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions