microsoft / microsoft/agent-framework

Python: Mixed pause recovery can lose provider outbox state and mis-correlate Host results

Open
#8,436 1 comment 0 reactions 1 assignee View on GitHub

@eavanvalkenburg is already working on this.

Since Sep 18, 2026.

agents python reproduced
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

Summary

Post-merge validation of #8428 found three follow-up failure modes in mixed approval/Host pause recovery:

  1. a completed mixed batch loses its replayable results if the provider invalidates the delivery request;
  2. stateful correlation can leave the batch permanently pending when two Host occurrences reuse one call_id and return equal payloads;
  3. stateless correlation accepts conflicting Host results for one occurrence.

The original #8428 fix remains valuable: complete-batch classification, session-backed pending pauses, stale-history isolation, OAuth streaming liveness, and single-owner streaming metadata all behave correctly. These cases arise after that boundary.

I reproduced all three on current main@234236b5b.

Reproduction

Provider invalidation after local execution
  1. A model batch contains one approval-required local call and one Host-owned call.
  2. The caller supplies the complete response set.
  3. The approved local tool executes once.
  4. The follow-up provider request raises ResponseInvalidatedException.
  5. Retry the logical operation with the same session.

The completed mixed-batch snapshot is removed before the provider confirms receipt. The retry can no longer replay the approved local result or the original charged budget, although the local side effect has already happened.

Expected behavior: retain a serializable provider outbox until provider success, then replay the stored Host/local results without re-authorizing or re-executing the local tool.

Reused call_id with identified and id-less Host results

Create two Host-owned occurrences with distinct Content.id values but the same provider call_id. Return equal result payloads, one carrying its occurrence id and one using the id-less compatibility path.

After the identified result fills its slot, the id-less result still considers both the answered equal slot and the remaining unanswered slot. Correlation is therefore ambiguous and the batch remains pending.

Expected behavior: reserve occurrence-identified responses first, then assign id-less responses only to the remaining unique unanswered occurrence.

Conflicting stateless Host results

For one Host occurrence, provide two identified function_result contents with the same occurrence id and call_id, but different payloads.

The first result removes the request from unmatched_host_requests; the second result is ignored rather than compared. The batch is reported complete while both contradictory results remain in the transcript.

Expected behavior: equivalent duplicates are inert replays; conflicting duplicates fail closed before the provider call.

Root cause

  • The completed mixed-pause state is cleared while approval replay is normalized, before provider delivery succeeds.
  • Stateful id-less Host matching includes an already answered equal-payload slot when assigning another occurrence.
  • Stateless Host matching does not retain the first response per occurrence for duplicate/conflict comparison.

Validation

On unmodified main@234236b5b, three focused regression tests fail with the behaviors above.

A local focused candidate makes those tests pass and additionally covers:

  • streaming and non-streaming provider invalidation;
  • JSON session round-trip before retry;
  • exactly-once local execution and streaming result publication;
  • preservation of the already-charged max_function_calls budget;
  • both arrival orders for identified and id-less equal Host results;
  • fail-closed stateless conflict handling.

Validation on the local candidate:

  • focused attack matrix: passed;
  • full core suite: passed;
  • AG-UI suite: 1359 passed, 14 skipped;
  • OpenAI, Declarative, and Foundry Hosting suites: passed;
  • Ruff, formatting, targeted Pyright, prek, and git diff --check: passed.

The affected spec 004 rows are Approval and Host-owned calls and Provider-invalidated partial response output. The provider-invalidation decision record also needs to distinguish calls originating in the invalid response from delivery of results whose local side effects already completed.

I have the focused implementation and regression matrix ready locally. If the core team agrees that these three cases should remain one recovery-state change, I can open the PR.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.