Skip to content

Python: [Bug]: Magentic reset does not reset participant sessions, so agents keep their history across replans #9129

Closed

Description

Description

When a Magentic workflow stalls and the orchestrator resets and replans, participant agents keep their full earlier conversation. The reset is supposed to start each participant fresh.

MagenticAgentExecutor.handle_magentic_reset in python/packages/orchestrations/agent_framework_orchestrations/_magentic.py ends with:

# Reset sessions
self._agent_thread = self._agent.create_session()

Nothing reads _agent_thread anymore. AgentExecutor runs the agent with self._session, which #3850 introduced in place of _agent_thread, but this assignment wasn't updated. So the new session is created and thrown away, and the agent keeps using its old session, including whatever its history provider stored before the reset.

The intended behavior is documented in the same class. Its docstring says "the agent threads and other states are reset when requested by the orchestrator", and the handler says it "resets the internal state of the agent executor, including any threads or caches, to prepare for a fresh start after replanning".

Expected: after a reset, a participant's next model call contains only what the orchestrator sends after the replan.

Code Sample

import asyncio
from typing import Any

from agent_framework import Agent, BaseChatClient, ChatResponse, Message
from agent_framework.orchestrations import (
    MagenticBuilder,
    MagenticContext,
    MagenticManagerBase,
    MagenticProgressLedger,
    MagenticProgressLedgerItem,
)


class RecordingClient(BaseChatClient):
    """Offline chat client that records what the agent sends to the model."""

    def __init__(self) -> None:
        super().__init__()
        self.calls: list[list[str]] = []

    def _inner_get_response(self, *, messages: Any, stream: bool, options: Any, **kwargs: Any) -> Any:
        self.calls.append([m.text for m in messages])
        reply = f"reply-{len(self.calls)}"

        async def _get() -> ChatResponse:
            return ChatResponse(messages=Message("assistant", [reply]))

        return _get()


class StallOnceManager(MagenticManagerBase):
    """Asks agentA, stalls once (forcing a reset and replan), asks agentA again, then finishes."""

    def __init__(self) -> None:
        super().__init__(max_round_count=10, max_stall_count=0)
        self.rounds = 0

    async def plan(self, magentic_context: MagenticContext) -> Message:
        return Message("assistant", ["plan"])

    async def replan(self, magentic_context: MagenticContext) -> Message:
        return Message("assistant", ["new plan"])

    async def create_progress_ledger(self, magentic_context: MagenticContext) -> MagenticProgressLedger:
        self.rounds += 1
        progress = self.rounds != 2  # round 2 stalls, which resets and replans
        item = MagenticProgressLedgerItem
        return MagenticProgressLedger(
            is_request_satisfied=item(reason="r", answer=self.rounds >= 4),
            is_in_loop=item(reason="r", answer=not progress),
            is_progress_being_made=item(reason="r", answer=progress),
            next_speaker=item(reason="r", answer="agentA"),
            instruction_or_question=item(reason="r", answer="before reset" if self.rounds == 1 else "after reset"),
        )

    async def prepare_final_answer(self, magentic_context: MagenticContext) -> Message:
        return Message("assistant", ["final"])


async def main() -> None:
    client = RecordingClient()
    agent = Agent(client=client, name="agentA", description="worker")
    workflow = MagenticBuilder(participants=[agent], manager=StallOnceManager()).build()
    await workflow.run("do the task")
    for number, call in enumerate(client.calls, 1):
        print(f"agentA model call {number}: {call}")


asyncio.run(main())

Output on main:

agentA model call 1: ['before reset']
agentA model call 2: ['before reset', 'reply-1', 'after reset']

Expected:

agentA model call 1: ['before reset']
agentA model call 2: ['after reset']

Error Messages / Stack Traces

None. The old history is silently sent again after every reset, so token use also grows with each replan.

Package Versions

agent-framework-orchestrations: 1.3.0, agent-framework-core: 1.20.0 (source checkout of main at 279d97f)

Python Version

Python 3.13

Additional Context

  • Reproduced on Windows and Linux.
  • No existing test covers what a participant sees after a reset. The reset tests cover the orchestrator's own context and the reset limit, not the participants.
  • Fix: assign self._session instead of self._agent_thread. The handler also clears the other pending-request state but not _pending_request_order, so I'd clear that too. I'll open a PR with this and a regression test.

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 7, 2026
  2. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Oct 7, 2026
  3. github-actions commented on Oct 7, 2026

    @github-actions
    Contributor

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

    Agent analysis

    Repro: python/packages/orchestrations/agent_framework_orchestrations/_magentic.py::MagenticAgentExecutor.handle_magentic_reset at lines 1361-1378 stores the replacement session in _agent_thread, while AgentExecutor continues passing _session to participant runs. A stall-triggered reset/replan therefore replays pre-reset participant history. Minimal repro: use an offline recording BaseChatClient, force one stall with max_stall_count=0, invoke the participant before and after replanning, and assert the second call contains only the post-reset instruction.

    • Failing test: python/packages/orchestrations/tests/test_magentic.py::test_magentic_reset_clears_participant_session_history
    • Files examined: python/packages/orchestrations/agent_framework_orchestrations/_magentic.py, python/packages/core/agent_framework/_workflows/_agent_executor.py, python/packages/orchestrations/tests/test_magentic.py, python/packages/orchestrations/pyproject.toml
    • Tests run: test_magentic_reset_clears_participant_session_history (failed), existing Magentic reset tests (passed)
    • Reported version: agent-framework-orchestrations 1.3.0, agent-framework-core 1.20.0
    • Current version: agent-framework-orchestrations 1.3.0
  4. added
    orchestrationUsage: [Issues, PRs], Target: multi-agent orchestration (high-level patterns)
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

orchestrationUsage: [Issues, PRs], Target: multi-agent orchestration (high-level patterns)pythonUsage: [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