microsoft / microsoft/agent-framework
Python: Mixed pause recovery can lose provider outbox state and mis-correlate Host results
@eavanvalkenburg is already working on this.
Since Sep 18, 2026.
- 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:
- a completed mixed batch loses its replayable results if the provider invalidates the delivery request;
- stateful correlation can leave the batch permanently pending when two Host occurrences reuse one
call_idand return equal payloads; - 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
- A model batch contains one approval-required local call and one Host-owned call.
- The caller supplies the complete response set.
- The approved local tool executes once.
- The follow-up provider request raises
ResponseInvalidatedException. - 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_callsbudget; - 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, andgit 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.