Skip to content

Python: [Bug]: Closing a FunctionalWorkflowAgent stream leaves the workflow marked as running #10164

Closed

Description

Observed Behavior

Closing the response stream returned by FunctionalWorkflow.as_agent().run(..., stream=True) leaves the underlying workflow marked as running. A subsequent call on the same caller-scoped agent raises RuntimeError, even though await response.close() has returned.

This affects cancellation or disconnect cleanup followed by another turn. Closing before the first pull reproduces it without executing the workflow function. Closing after the first output also makes an immediate follow-up run fail.

Expected Behavior

Explicitly closing the agent response stream should close its owned workflow stream and release the run state before returning. The next sequential run should succeed, as it does when closing a stream returned directly by FunctionalWorkflow.run().

Steps to Reproduce

  1. Use agent-framework-core from commit 924a6e50ec0336f3c965ee8b1151c54085888b6e (current main, package version 1.25.0).
  2. Run the snippet below. No model, credentials or external service is needed.
  3. The second run raises instead of returning result: 2.

Minimal Reproduction

import asyncio

from agent_framework import workflow


@workflow
async def pipeline(value: int) -> str:
    return f"result: {value}"


async def main() -> None:
    agent = pipeline.build().as_agent()
    response = agent.run(1, stream=True)
    await response.close()
    print((await agent.run(2)).text)


asyncio.run(main())

Error Messages and Stack Traces

  File "agent_framework/_workflows/_functional.py", line 2062, in run
    self._ensure_not_running()
  File "agent_framework/_workflows/_functional.py", line 2664, in _ensure_not_running
    raise RuntimeError("Workflow is already running. Concurrent executions are not allowed.")
RuntimeError: Workflow is already running. Concurrent executions are not allowed.

Package Versions

  • agent-framework-core: 1.25.0, installed from the unmodified checkout above
  • msgspec: 0.22.0; pydantic: 2.14.3; opentelemetry-api: 1.45.2

Python Version

CPython 3.12.13

Operating System

macOS, arm64

Regression

Unknown

Additional Context

Five local controls used the same workflow and a fresh instance for each case:

Stream path and close point Immediate next run
Direct workflow stream, before first pull Succeeds
Agent wrapper stream, before first pull Raises RuntimeError
Direct workflow stream, after first workflow event Succeeds
Agent wrapper stream, after first output Raises RuntimeError
Agent wrapper stream, after full consumption Succeeds

After deleting the closed stream, calling gc.collect() and yielding three event-loop ticks, the before-first-pull agent case still raises. The partially consumed agent case recovers only after that delayed cleanup.

FunctionalWorkflowAgent._run_streaming() eagerly creates the inner workflow stream, acquiring the run guard. It then returns a separate ResponseStream around _generate_updates(), with no cleanup hook or finally that closes the inner stream. The direct workflow stream has the cleanup hook that releases its guard.

This concerns the current experimental FUNCTIONAL_WORKFLOWS adapter. #8949 tracks the broader vNext design; this report asks whether propagating close to the owned inner stream is an appropriate bounded fix for the current API. No implementation has been started. The direct-workflow ContextVar issue in #7787 is separate; the direct close controls above pass.

I personally ran the minimal reproduction above on this checkout and observed the reported RuntimeError. Codex assisted with the source analysis and report preparation, and executed the additional five-case controls locally.

Acknowledgements

  • I searched existing issues and did not find a duplicate.
  • I personally verified this behavior and the reproduction details are authentic.
  • I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.

Activity

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

    @github-actions
    Contributor

    Reproduced on main (924a6e5, core 1.25.0) with an independently written test based on the report: after await agent.run(1, stream=True).close(), agent.run(2) raises RuntimeError: Workflow is already running. Routed to the workflows owner.

    Support boundary: unclear. This is the experimental functional-workflow adapter, and I found no maintained spec or decision record that sets the scope. A maintainer should decide. Whether propagating close to the inner stream is the right fix is also for maintainers.

    Run: https://github.com/microsoft/agent-framework/actions/runs/42361134794

    Generated by Issue Routing for #10164 · copilot · auto · ~18 AIC · ⌖ ~1 AIC · ⊞ 5.2K · ◷

  4. Yongthyuan commented on Nov 7, 2026

    @Yongthyuan
    ContributorAuthor

    A tested patch is available in my fork: Yongthyuan@f61d189.

    It registers the owned workflow stream's close() as a cleanup hook on the outer agent response. This uses the existing stream cleanup path rather than clearing the workflow's run guard directly.

    The added tests reproduce the failure before the fix for both an unstarted and partially consumed stream, and check normal completion, repeated close while a newer run is active, workflow errors, and cancellation. With the fix, 249 functional workflow tests and 114 ResponseStream tests pass; source/test Pyright and pinned Ruff checks pass. The original minimal reproduction now prints result: 2, and all five original comparison cases can run again immediately after close.

    I have kept this as a fork patch while waiting for maintainer direction on #10164 and the current experimental adapter. No PR has been opened. Codex assisted with implementation, tests, and this note; the account owner personally verified the original failure.

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

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: Pythonready-for-implementationMaintainers agree the problem and direction are ready for implementation.reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowworkflowsUsage: [Issues, PRs], Target: Workflows

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions