Skip to content

fix(flows): Drop orphaned FRs instead of poisoning sessions - #6587

Open
a2105z wants to merge 1 commit into
google:mainfrom
a2105z:fix/orphaned-function-response-poisoning
Open

fix(flows): Drop orphaned FRs instead of poisoning sessions#6587
a2105z wants to merge 1 commit into
google:mainfrom
a2105z:fix/orphaned-function-response-poisoning

Conversation

@a2105z

@a2105z a2105z commented Aug 4, 2026

Copy link
Copy Markdown

Link to Issue or Description of Change

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

Problem:
If a session ever persisted a function response whose matching function call was absent from the branch-filtered history, _rearrange_events_for_latest_function_response raised ValueError while building llm_request.contents.

That raise happens in request preprocessing — before before_model_callback — so no user hook can heal it. Every later turn replays the same history and re-raises, permanently poisoning the session.

Solution:
When the trailing event carries function responses with no matching function call in history, drop those orphaned response event(s), log a warning, and continue rearranging / assembling contents from the surviving history. The model simply never sees a result it never asked for, and the session stays usable.

Testing Plan

Unit Tests:

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

Updated / new tests in tests/unittests/flows/llm_flows/test_contents_function.py:

pytest results (local):

pytest tests/unittests/flows/llm_flows/test_contents_function.py -v
# 10 passed

pytest tests/unittests/flows/llm_flows/test_contents_function.py \
       tests/unittests/flows/llm_flows/test_contents.py \
       tests/unittests/apps/test_compaction_runner_e2e.py -q
# 45 passed

pytest tests/unittests/flows/llm_flows/ -q
# 485 passed

Manual / repro verification:

Pre-commit:

pre-commit run --files \
  src/google/adk/flows/llm_flows/contents.py \
  tests/unittests/flows/llm_flows/test_contents_function.py
# all hooks passed

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

Files changed:

  • src/google/adk/flows/llm_flows/contents.py — drop orphaned trailing FRs instead of raising
  • tests/unittests/flows/llm_flows/test_contents_function.py — regression + runner coverage

Companion producer-side issues (e.g. progressive SSE leaving orphaned FRs) are separate; this change hardens the consumption path so any orphaned FR no longer kills the session.

Exact commands run

pytest tests/unittests/flows/llm_flows/test_contents_function.py -v
# 10 passed

pytest tests/unittests/flows/llm_flows/test_contents_function.py \
       tests/unittests/flows/llm_flows/test_contents.py \
       tests/unittests/apps/test_compaction_runner_e2e.py -q
# 45 passed

pytest tests/unittests/flows/llm_flows/ -q
# 485 passed

pre-commit run --files \
  src/google/adk/flows/llm_flows/contents.py \
  tests/unittests/flows/llm_flows/test_contents_function.py
# all hooks passed

When the latest history event was a function response with no matching
function call, contents assembly raised ValueError before any callback
could intervene, leaving the session permanently unusable. Drop the
orphaned response and continue so later turns can proceed.

Fixes google#6582
@a2105z

a2105z commented Aug 4, 2026

Copy link
Copy Markdown
Author

Hi @GWeale — hoping you might have a minute when free. You’ve touched contents.py a lot recently, so I wanted to check this with you.

This is a fix for #6582: an orphaned trailing function response used to raise during contents assembly (before any callback), which permanently poisoned the session. The change drops those unmatched FRs, logs a warning, and continues so later turns can still run.

I tried to keep the blast radius small and covered it with contents-processor + full Runner tests. Happy to adjust if you’d rather surface this differently — just say the word. Thanks so much for any feedback.

@a2105z

a2105z commented Aug 4, 2026

Copy link
Copy Markdown
Author

Hi @DeanChensj — looping you in as well for a review when you have a moment.

This is the consumption-side companion to the mixed-turn session-poisoning work: orphaned trailing function responses were raising during contents assembly and leaving the session unusable. The change drops those unmatched FRs and continues.

Happy to tweak anything that looks off — thanks so much for taking a look.

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

Labels

core [Component] This issue is related to the core interface and implementation

Projects

None yet

3 participants