Skip to content

Python: Python : checkpoint restore +pending request deserialization failure when hosting workflow as agent with ResponsesHostserver in foundry #7618

Closed

Description

@dgsear

Scenario

This occurs when a workflow is hosted as an agent via ResponseHostServer (Response protocol), aand the workflow is resumed across requests using previous_response_id

when it happened

  • During the hosted-agent execution (work-as-agent ), sfter a prior turn produced request_info and pending requests.
  • on the next /responses call with previous_response_id + function_call_output, the server restored checkpoint state and failed on deserializing a pending request type.

observed behaviour

  • checkpoint decode warning:
    checkpoint deserialization blocked for type 'agent_framework_orchestrations._handoff:HandoffAgentUserRequest'
    checkpoint deserialization blocked for type agent_framework_orchestrations._magentic:MagenticPlanReviewRequest

  • Restore proceeded from an older/other checkpoint state

  • Follow-up response was rejected

  • `ValueError :Response provided for unknown request Id :<request_id>

Expected

When hosting workflow as agent:
-pending request checkpoint state should restore reliably across turns

Package versions:
agent-framework-core==1.13.0
agent-framework-foundry-hosting==1.0.0.b260730

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Aug 11, 2026
  2. changed the title [-]Python : checkpoint restore +pending request deserialization failure when hosting workflow as agent with ResponsesHostserver in foundry[/-] [+]Python: Python : checkpoint restore +pending request deserialization failure when hosting workflow as agent with ResponsesHostserver in foundry[/+] on Aug 11, 2026
  3. added
    hostingUsage: [Issues, PRs], Target: all hosting related solutions
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Aug 11, 2026
  4. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Aug 12, 2026
  5. github-actions commented on Aug 12, 2026

    @github-actions
    Contributor

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

    Agent analysis

    Repro: _RestrictedUnpickler.find_class() in packages/core/agent_framework/_workflows/_checkpoint_encoding.py (line 200) rejects types from agent_framework_orchestrations.* because _FRAMEWORK_MODULE_PREFIX on line 72 is "agent_framework." which doesn't match "agent_framework_orchestrations." (underscore vs dot). Triggered when FileCheckpointStorage (or any storage passing allowed_types != None) deserializes a checkpoint containing HandoffAgentUserRequest or MagenticPlanReviewRequest. Minimal repro: encode a HandoffAgentUserRequest via encode_checkpoint_value then decode with allowed_types=frozenset() — the restricted unpickler raises "deserialization blocked".

    • Failing test: python/packages/core/tests/workflow/test_checkpoint_orchestrations_deserialization.py
    • Files examined: python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py, python/packages/core/agent_framework/_workflows/_checkpoint.py, python/packages/foundry_hosting/agent_framework_foundry_hosting/_state_store.py, python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py, python/packages/orchestrations/agent_framework_orchestrations/_handoff.py
    • Tests run: python/packages/core/tests/workflow/test_checkpoint_orchestrations_deserialization.py
    • Reported version: agent-framework-core==1.13.0, agent-framework-foundry-hosting==1.0.0.b260730
    • Current version: agent-framework-core==1.13.0, agent-framework-foundry-hosting==1.0.0b260730
  6. uuzzrm commented on Aug 12, 2026

    @uuzzrm
    Contributor

    I can prepare a focused fix for this. The triage notes point to the checkpoint allowlist rejecting �gent_framework_orchestrations.* because it only matches the dotted �gent_framework. prefix. I’ll verify the repro on current main, add a regression test for the affected pending request types, and keep the change limited to checkpoint deserialization.

  7. ktz03 commented on Oct 18, 2026

    @ktz03
    Contributor

    A normal HandoffBuilder user-input pause still cannot be saved with default FileCheckpointStorage on source main ebcf4db7f9462ef6c377761e0b3159c4c5a73027, although the orchestration registrations from #8258 are present. This looks like additional evidence for this existing orchestration-checkpoint issue rather than a separate issue or a competing implementation.

    The workflow itself supplies a list[Message] response type for HandoffAgentUserRequest. At the pause boundary, file checkpoint save-time validation reports types:GenericAlias; the pending-request checkpoint is not saved. The latest successfully saved checkpoint contains no pending request, so attempting the normal reply against that checkpoint then raises No pending requests found in workflow context. This is a failed pause save, not corruption of a successfully saved pause checkpoint.

    The same ordinary handoff and reply with InMemoryCheckpointStorage saves one pending request and resumes successfully. The file case saves zero pending requests in its latest available checkpoint. Both use real Agent, function/middleware layers, public HandoffBuilder, and deterministic local chat responses, with the required per-service-call history persistence enabled; there is no application state, custom codec, extra allowed type or fault injection.

    Minimal reproduction

    Run with both Core and orchestrations resolving to the source revision above:

    import asyncio
    from pathlib import Path
    from tempfile import TemporaryDirectory
    
    from agent_framework import (
        Agent, BaseChatClient, ChatMiddlewareLayer, ChatResponse, Content,
        FileCheckpointStorage, FunctionInvocationLayer, InMemoryCheckpointStorage, Message,
    )
    from agent_framework_orchestrations import HandoffAgentUserRequest, HandoffBuilder
    
    
    class LocalClient(FunctionInvocationLayer, ChatMiddlewareLayer, BaseChatClient):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        async def _inner_get_response(self, *, messages, stream, options, **kwargs):
            await self._validate_options(options)
            assert not stream
            if any(m.text == "Tuesday" for m in messages):
                content = Content.from_text("Delivery confirmed.")
            elif self.name == "dispatch":
                content = Content.from_function_call(
                    call_id="delivery-handoff", name="handoff_to_calendar", arguments={}
                )
            else:
                content = Content.from_text("Which delivery date should I record?")
            return ChatResponse(messages=[Message(role="assistant", contents=[content])])
    
    
    def build(storage):
        agents = [
            Agent(client=LocalClient(name), name=name,
                  require_per_service_call_history_persistence=True)
            for name in ("dispatch", "calendar")
        ]
        return HandoffBuilder(
            name="delivery", participants=agents, checkpoint_storage=storage,
            termination_condition=lambda conversation: any(
                m.text == "Delivery confirmed." for m in conversation
            ),
        ).with_start_agent(agents[0]).build()
    
    
    async def check(storage):
        initial = await build(storage).run("Arrange delivery of the blue desk.")
        request = initial.get_request_info_events()[0]
        checkpoint = await storage.get_latest(workflow_name="delivery")
        print(type(storage).__name__, "saved pending requests:",
              len(checkpoint.pending_request_info_events))
        result = await build(storage).run(
            checkpoint_id=checkpoint.checkpoint_id,
            responses={request.request_id: HandoffAgentUserRequest.create_response("Tuesday")},
        )
        print([value.text for value in result.get_outputs() if hasattr(value, "text")])
    
    
    async def main():
        await check(InMemoryCheckpointStorage())
        with TemporaryDirectory() as directory:
            await check(FileCheckpointStorage(Path(directory)))
    
    
    asyncio.run(main())

    Observed output:

    InMemoryCheckpointStorage saved pending requests: 1
    ['Delivery confirmed.']
    FileCheckpointStorage saved pending requests: 0
    

    The file run logs this during checkpoint creation, then the reply raises the pending-request error:

    Failed to create checkpoint at iteration 2: Failed to decode pickled checkpoint data:
    Checkpoint deserialization blocked for type 'types:GenericAlias'.
    ...
    RuntimeError: No pending requests found in workflow context.
    

    Tested on Windows / Python 3.14.3, using source Core 1.22.0 and orchestrations 1.3.2 with cached dependencies (installed Core metadata 1.19.0). A second independent local business example produced the same memory/file difference. This does not cover live models, Foundry hosting, AG-UI, streaming, other Python versions or a full suite.

    Related scope: #7618 / #7789 and the already merged #8258 cover built-in orchestration checkpoint types; #7831 owns the broader recovery/fallback behavior. #9440 needs an explicitly allowed application ZoneInfo value, whereas this example contains only framework-generated response types and ordinary text. Could maintainers confirm the direction for this remaining built-in Handoff case within the existing route? No implementation or allowlist change is included.

    AI assistance: analysis, reproduction, and writing.

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

Metadata

Metadata

Labels

hostingUsage: [Issues, PRs], Target: all hosting related solutionspythonUsage: [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