microsoft / microsoft/agent-framework

.NET: [Bug]: Concurrency capability is not preserved by function and subworkflow bindings

Open
#8,341 1 comment 0 reactions 1 assignee View on GitHub

@peibekwe is already working on this.

Since Sep 13, 2026.

.NET reproduced workflows
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.