Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Executor instances with the same name in different workflows silently collide

Open
#74 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
csharp

Research direction

Start with ExecutorRegistry.Register and ServiceCollectionExtensions.BuildWorkflowRegistrationRecursive, then trace the name-only dispatch shown in the issue. Compare registrations by RawValue reference equality, preserving shared-instance reuse while reporting distinct-instance conflicts with both workflow names. Add coverage for both cases and verify derived activity names remain unchanged.

Written by the indexing model from the issue text.

Description

workflows

Summary

Executors are registered and dispatched through a single global, name-keyed namespace with no workflow scoping. When two workflows contain different executor instances that happen to share a name, the second registration is silently dropped and that workflow's executor runs the first workflow's code.

Sharing one executor instance across multiple workflows is supported, works today, and must keep working. This issue is not about that case.

Same failure class as #50, but it needs no duplicate workflow names, so #66 does not address it. Spotted by @ahmedmuhsin while reviewing #66.

Repro

  1. Build two workflows, each with its own executor instance under the same name:

    var a = new WorkflowBuilder(new FunctionExecutor<string>("Step", HandlerA)).WithName("WorkflowA").Build();
    var b = new WorkflowBuilder(new FunctionExecutor<string>("Step", HandlerB)).WithName("WorkflowB").Build();
    
  2. Register both:

    options.AddWorkflow(a);
    options.AddWorkflow(b);
    
  3. Run WorkflowB.

Expected: either WorkflowB's Step runs HandlerB, or the conflict is reported at registration.

Actual: WorkflowB's Step runs HandlerA. No error at registration or at runtime.

workflows=2 executors=1
'Step' boundToA=True  boundToB=False
Contrast: the shared-instance case works
FunctionExecutor<string> shared = new("Step", Handler);
var a = new WorkflowBuilder(shared).WithName("WorkflowA").Build();
var b = new WorkflowBuilder(shared).WithName("WorkflowB").Build();

Also gives Executors.Count == 1, but here that is correct: binding.RawValue is reference-equal across both workflows, so collapsing to one registration preserves behavior. Core MAF places no ownership restriction on reusing an executor instance across workflows, unlike using a Workflow as a subworkflow of multiple parents.

RawValue reference equality is therefore what separates legitimate reuse from a real collision.

Root cause

Two places key on the bare executor name and keep the first writer:

  1. ExecutorRegistry.Register — TryAdd drops later registrations of the same name without error:

    this._executors.TryAdd(executorName, new ExecutorRegistration(executorId, binding));
    
  2. ServiceCollectionExtensions.BuildWorkflowRegistrationRecursive — one HashSet<string> registeredActivities is threaded through every workflow, and activity names are derived as dafx-{executorName}, so dafx-Step is registered once globally against the first workflow's binding.

Dispatch is name-only, with no workflow context available to disambiguate:

string executorName = WorkflowNamingHelper.ToWorkflowName(activityFunctionName);
if (!durableOptions.Workflows.Executors.TryGetExecutor(executorName, out ExecutorRegistration? registration))

Impact

Silent and result-affecting rather than a startup failure — both workflows appear registered and runnable, and the problem surfaces only as wrong results at runtime. Names like Step, Start, Process, or Validate are natural choices and likely to collide across independently authored workflows.

Suggested fix

In ExecutorRegistry.Register, when executorName is already registered:

  • if ReferenceEquals(existing.Binding.RawValue, binding.RawValue), treat as reuse and no-op;
  • otherwise throw, naming the executor and both workflows.

This mirrors what #66 did for duplicate workflow names, keeps the shared-instance scenario working, and leaves derived activity names untouched so in-flight orchestration histories stay valid.

Scoping keys by workflow (e.g. {workflowName}/{executorName}) was considered and rejected: it changes every derived activity name, breaking in-flight orchestrations, and registers a shared executor once per referencing workflow for no benefit.

Caveat to settle first: an executor supplied per workflow via a factory produces distinct instances that are logically the same executor, and a reference check would reject it. Same limitation #66 has for calling Build() twice. If that pattern needs support, this needs an identity concept beyond reference equality.

Acceptance criteria

  • Reusing a single executor instance across multiple workflows continues to work, covered by a test.
  • Two workflows containing different executor instances that share a name no longer silently cross-wire; the conflict is reported at registration time, naming the executor and the workflows involved.
  • Derived activity names are unchanged, or any change is called out for backward compatibility with in-flight orchestrations.
Dominant language
Python
Stars
19
Forks
11
Avg merge
5d 17h
Merged PRs (30d)
15

Getting set up

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.

More from microsoft/agent-framework-durable-extension

All issues in microsoft/agent-framework-durable-extension

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.