microsoft / microsoft/agent-framework
.NET: [Bug]: Concurrency capability is not preserved by function and subworkflow bindings
@peibekwe is already working on this.
Since Sep 13, 2026.
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
### Description
`ExecutorBinding.SupportsConcurrentSharedExecution` contributes to whether a workflow can be used with `InProcessExecution.Concurrent`.
I found two binding paths where the underlying concurrency capability is not preserved:
1. `BindAsExecutor(..., threadsafe: false)` creates a `FunctionExecutor` that is not cross-run shareable, but the configured binding currently reports concurrent shared execution as supported.
2. `SubworkflowBinding` reports concurrent shared execution as supported even when its child workflow is not concurrent-capable.
As a result, workflows can be accepted by the concurrent execution path even though one of the shared execution paths does not support concurrent runs.
The subworkflow case is independently reproducible using a direct non-shareable executor, so it does not depend on the function-binding behavior.
Expected behavior:
- `threadsafe: false`, including the default value, should make a shared function binding ineligible for concurrent workflow runs.
- `threadsafe: true` should remain eligible.
- A subworkflow binding should reflect whether its child workflow supports concurrent runs.
- Invalid concurrent execution should be rejected at the appropriate workflow boundary.
### Code Sample
#### Function binding
```csharp
Func handler = static value => value;
ExecutorBinding binding =
handler.BindAsExecutor("Function", threadsafe: false);
Workflow workflow =
new WorkflowBuilder(binding).Build();
await using StreamingRun run =
await InProcessExecution.Concurrent.OpenStreamingAsync(workflow);
// Actual: the concurrent run is accepted.
// Expected: InvalidOperationException.
```
#### Independent subworkflow case
```csharp
static ValueTask HandleAsync(
int message,
IWorkflowContext context,
CancellationToken cancellationToken) => default;
FunctionExecutor childExecutor =
new("Child", HandleAsync, declareCrossRunShareable: false);
Workflow child =
new WorkflowBuilder(childExecutor.BindExecutor()).Build();
ExecutorBinding childBinding =
child.BindAsExecutor("ChildWorkflow");
Workflow parent =
new WorkflowBuilder(childBinding).Build();
await using StreamingRun run =
await InProcessExecution.Concurrent.OpenStreamingAsync(parent);
// Actual: the parent passes the concurrent eligibility check.
// Expected: the parent is rejected because its child workflow is not
// concurrent-capable.
```
### Error Messages / Stack Traces
No exception is produced at the affected eligibility boundary.
The expected behavior is the existing `InvalidOperationException` used when concurrent execution is requested for a workflow containing a binding that does not support concurrent shared execution.
### Package Versions
Microsoft.Agents.AI.Workflows: 1.21.0
The same affected implementation is present in the `dotnet-1.21.0` source tag. Runtime reproduction below was performed against current `microsoft/agent-framework` `main`; the release tag was inspected at source level only.
### .NET Version
.NET SDK 10.0.401
### Additional Context
The `threadsafe` parameter already describes whether a function handler may be used simultaneously by multiple runs. The function case appears to lose that capability when the `FunctionExecutor` is converted into a configured binding.
The subworkflow case appears to be the same capability-propagation issue at the workflow boundary: the child workflow can be non-concurrent while the binding presented to its parent reports concurrent support.
I have a small patch and deterministic regression coverage for both cases. If maintainers would prefer the subworkflow case to be handled separately, I am happy to split it into a follow-up.
I'm happy to work on the fix if this direction looks appropriate.
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.