Extension command.execute can be accepted but never delivered, hanging slash-command invocation

Open
#2,665 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

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

Research direction

Start at the runtime command-event routing path, especially targeted-owner resolution, publish_session_event, post_response_notifications, gated_sessions, and the pending command response wait. Trace the extension reload/resume flow and add the requested regression coverage for immediate slash-command invocation; done means command.execute reaches its owner and completes, or delivery failure resolves promptly with an actionable error.

Written by the indexing model from the issue text.

Description

bug runtime runtime triage

Drafted by GIthub Copilot with human review.

Summary

An extension slash command can be recognized by Copilot CLI and dispatched as command.execute, yet never reach the owning extension handler. The invocation then hangs silently because the runtime waits indefinitely for session.commands.handlePendingCommand and no timeout or delivery error is surfaced.

This is exposed through the public extension SDK, but the captured failure occurs in runtime command-event routing before SDK handler entry. Please triage as a runtime-root-cause bug.

Environment

  • Copilot CLI: 1.0.84-8
  • OS: Windows 11
  • Extension API: @github/copilot-sdk/extension via joinSession({ commands: [...] })
  • Reproduced: September 15, 2026

Reproduction

  1. Load an extension that registers a slash command through joinSession, for example:

    await joinSession({
      commands: [{
        name: "bookmarks",
        description: "Open the session bookmark manager",
        handler: async () => {
          // Instrument handler entry here.
        },
      }],
    });
    
  2. Reload extensions.

  3. Confirm the extension reaches === ready === and can service another registered callback. In this reproduction, its canvas provider successfully handled rehydration.

  4. Invoke /bookmarks interactively.

Actual behavior

  • CLI recognizes and invokes the registered slash command.
  • Runtime emits command.execute event ID 537170aa-2e69-41b6-a37b-ad0e505ea166.
  • The detached host-delivery queue starts and reports successful completion.
  • The owning extension never enters the command handler.
  • No command.completed event is emitted.
  • No No client found for command error is emitted.
  • The invocation remains pending indefinitely without user-visible failure.

Relevant runtime timeline:

2026-09-15T08:44:56.142Z slash_command_invoked
2026-09-15T08:44:56.143Z Detached host delivery started
  delivery_kind="reserved_event"
  event_type="command.execute"
  event_id="537170aa-2e69-41b6-a37b-ad0e505ea166"
2026-09-15T08:44:56.146Z Detached host delivery completed
2026-09-15T08:44:56.146Z Detached host delivery retirement decision completed

Temporary instrumentation around command-handler entry, session.rpc.canvas.open, and the canvas provider confirmed that neither the command handler nor downstream canvas call was reached. The same extension process had successfully handled canvas-provider rehydration approximately 35 seconds earlier.

Expected behavior

One of the following should happen deterministically:

  1. command.execute reaches the registered owner, the handler runs, and command.completed resolves the invocation; or
  2. Delivery failure/disconnection is detected and the pending invocation fails promptly with an actionable error.

A request-like control-plane event must not be considered successfully delivered merely because it was queued somewhere that may never drain.

Suspected runtime area

Runtime source currently:

  • resolves a targeted owner for command.execute;
  • calls publish_session_event for that connection;
  • treats an event queued in post_response_notifications behind a gated_sessions dispatch token as successfully published; and
  • waits on the pending command response without a timeout.

This makes a stale or unreleased create/resume response gate one plausible mechanism: publication reports success, so no undelivered-command rejection occurs, but the owner never receives the event. The logs do not expose the connection ID or gate token, so this is a hypothesis rather than a proven precise race.

Relationship to #2368

This appears to be a sibling or incomplete-fix path for #2368, not necessarily the identical implementation defect:

  • #2368 concerned lost external_tool.requested events and SDK event-forwarder lag.
  • This reproduction concerns targeted command.execute delivery after extension reload.
  • Both turn a lost request-like event into an indefinite hang because no timeout or disconnect failure resolves the pending request.

Requested fix

  • Guarantee delivery or explicit rejection for targeted command.execute events.
  • Do not treat indefinitely gated notifications as successful delivery.
  • Add a bounded timeout/disconnection failure for pending command execution as defense in depth.
  • Add a regression test covering extension reload/resume followed by immediate slash-command invocation.
Dominant language
Java
Stars
10.5k
Forks
1.5k
Avg merge
1d 12h
Merged PRs (30d)
133

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.

More from github/copilot-sdk

All issues in github/copilot-sdk

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.