OpenHands / OpenHands/software-agent-sdk

[Bug]: Process-global subagent registry leaks definitions across conversations

Open
#4,661 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

architecture bug plugins priority:medium ready-for-dev
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Bug Description

File/plugin subagent definitions are conversation-specific, but every factory is stored in one process-global first-writer-wins registry (openhands-sdk/openhands/sdk/subagent/registry.py:56-58,124-152,288-367).

Each LocalConversation registers its plugin and workspace definitions into that registry (conversation/impl/local_conversation.py:1211-1216,1482-1499). Two conversations can therefore define the same subagent name differently; whichever initializes first silently controls both. Remote creation also serializes all definitions visible in the process (remote_conversation.py:807-833), which can include definitions from unrelated workspaces. Agent Server repeats the same global registration for per-conversation payloads (conversation_service.py:513-545).

Expected Behavior

Built-in/programmatic definitions may have process ownership, but plugin/project/user file definitions must resolve in the conversation that discovered them. Same-name definitions in different conversations must not collide or leak.

Actual Behavior

An independent, self-contained reproducer is posted in the issue comments. Save
it as /tmp/repro_4661.py, then run:

uv run python /tmp/repro_4661.py

On SDK commit 6d3881035, it exercises two real project trees and the actual file-agent registry; the test-only reset is used solely to run both initialization orders independently and prints:

first_project=project-a
first_registered=['shared-repro-agent']
second_registered=[]
global_winner=definition-from-A
remote_forwarded_definition=['definition-from-A']
first_project=project-b
first_registered=['shared-repro-agent']
second_registered=[]
global_winner=definition-from-B
remote_forwarded_definition=['definition-from-B']
Suggested Implementation

Retain a process-level base registry for explicit programmatic/built-in registrations and add a conversation-scoped overlay/resolver for plugin/project/user definitions. Task/delegate lookup and remote serialization should consume that resolver.

Acceptance Criteria
  • Two live local conversations can use same-name, different project definitions in either initialization order.
  • Two server conversations can do the same concurrently.
  • Remote creation serializes only definitions available to that conversation.
  • Existing precedence remains: programmatic > plugin > project > user > built-in.
  • Persisted/remote resume and task/delegate lookup remain compatible.
Version

Current main at 6d3881035. Found during the independent coverage pass of a read-only ownership audit.

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.

Research direction

Start with the reproducer at /tmp/repro_4661.py, then trace registry.py, local_conversation.py, remote_conversation.py, and conversation_service.py at the cited ranges. Follow how definitions are registered, resolved, serialized, resumed, and used for task/delegate lookup. Done means the listed acceptance checks pass for local, server, remote, precedence, and resume behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.