Skip to content

[BUG]: extensions_reload and extensions_manage tools deadlock permanently when project has a joinSession() extension #2348

@carter003

Description

@carter003

Describe the bug

Calling extensions_reload or extensions_manage tools in a session where a project-level extension (using joinSession() from @github/copilot-sdk/extension) is active results in a permanent hang that requires the user to manually cancel the tool call. The CLI never recovers from the hang — the tool call must be interrupted every time.

Root cause analysis

The deadlock is caused by a circular IPC dependency during the reload sequence:

  1. extensions_reload is invoked → the CLI main process blocks synchronously, waiting for the reload to complete.
  2. The existing extension subprocess is terminated.
  3. A new extension_bootstrap.mjs subprocess is spawned and loads the extension module.
  4. The extension module calls await joinSession() at the top level — this sends a handshake message back to the CLI main process to register tools and establish the session.
  5. The CLI main process cannot respond to the handshake because it is blocked in step 1.
  6. Permanent deadlock: parent waits for child, child waits for parent.

The same deadlock occurs with extensions_manage because it goes through the same extension management subsystem.

Affected version

GitHub Copilot CLI 1.0.12

Steps to reproduce

  1. Create a project-level extension in .github/extensions/my-ext/extension.mjs with a top-level await joinSession(...) call (standard SDK pattern from the documentation).
  2. Start a Copilot CLI session in that project directory. The extension loads successfully.
  3. In the session, call the extensions_reload tool (or extensions_manage with operation list/inspect).
  4. Observe: the tool call never returns. The CLI is permanently unresponsive until the tool call is manually cancelled.
  5. Repeat step 3 after cancelling — same result every time.

Expected behavior

  • extensions_reload should complete successfully and the extension should be re-registered.
  • If the reload mechanism requires a subprocess handshake, the main process should handle IPC messages asynchronously (non-blocking) during the reload wait, so the new extension subprocess can complete its joinSession() registration.

Additional context

Environment:

  • OS: Linux (Ubuntu)
  • Copilot CLI version: 1.0.12
  • Extension type: project-level (.github/extensions/), JS/ESM, using @github/copilot-sdk/extension

Workaround:

  • Skills and agent .md files are effective immediately on save (no reload needed).
  • For changes to extension.mjs JS code: close the terminal and re-open a new session.

Process evidence (at time of hang):

# Two extension_bootstrap.mjs subprocesses observed — both blocked
PID 230448  EXTENSION_PATH=.github/extensions/my-ext/extension.mjs  COPILOT_LOADER_PID=230374
PID 245405  EXTENSION_PATH=.github/extensions/my-ext/extension.mjs  COPILOT_LOADER_PID=210913

# stdin/stdout of each subprocess are sockets (IPC channels), confirming the
# joinSession() handshake is attempted but the parent is not reading from them.
fd 0 -> socket:[...]
fd 1 -> socket:[...]

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pluginsPlugin system, marketplace, hooks, skills, extensions, and custom agents

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions