Skip to content

Tool call hangs after extension startup fails #4670

Open

Description

@MattPD

Describe the bug

After I resumed a large session, an extension failed inside joinSession() before reporting ready, and its process exited.

The CLI continued to offer the extension's custom tool. I asked the CLI to call it. The CLI started the call, but the handler did not run and the call reported no error. I aborted it after 9 minutes 38 seconds.

This issue concerns cleanup and dispatch after extension startup failure. A separate large-session attachment defect triggered the failure in this run.

Environment

GitHub Copilot CLI 1.0.82
@github/copilot-sdk 1.0.9-preview.2
Node.js v24.18.1
Linux x86_64

Confirmed startup trigger

I traced the installed 1.0.82 app.js bundle. joinSession() sends a session.resume request containing registration options rather than session history. The host still serializes the resident session's full event array while attaching the extension:

  1. handleSessionResumeCore() handles session.resume and calls the host's initializeSession() with replayEvents: false.
  2. The host's initializeSession() ignores that option and passes session.getEvents() to seamHost.initializeSession().
  3. seamHost.initializeSession() calls JSON.stringify(events).

Node.js v24.18.1 rejects JavaScript strings longer than 536,870,888 UTF-16 code units and throws Invalid string length. I estimated each serialized event array's length from its session event log. Extension attachment succeeded below the limit and failed above it.

In the run that reproduced the stale registration, joinSession() failed with Invalid string length. That session's events.jsonl file was about 1.2 GB.

Honoring replayEvents: false would remove this trigger. Registration cleanup must still handle every startup failure.

Reduced extension

import { writeFile } from "node:fs/promises";
import { joinSession } from "@github/copilot-sdk/extension";

await joinSession({
    tools: [{
        name: "stale_tool_probe",
        description: "Verify extension tool lifecycle.",
        parameters: {
            type: "object",
            properties: {},
            additionalProperties: false,
        },
        skipPermission: true,
        handler: async () => {
            await writeFile(
                new URL("handler-ran.marker", import.meta.url),
                "handler ran\n",
            );
            return "handler-ran";
        },
    }],
});

Observed sequence

  1. I stored the reduced extension under ~/.copilot/session-state/<session-id>/extensions/stale-tool-probe/extension.mjs.
  2. I resumed the large session.
  3. joinSession() failed with Invalid string length before the extension reported ready, and the extension process exited.
  4. I asked the CLI to call stale_tool_probe. The CLI started the call instead of rejecting it as an unknown tool.
  5. The call was still pending when I aborted it. handler-ran.marker did not appear.

Expected behavior

  • The CLI offers an extension's registrations only while the extension is ready and connected.
  • Startup failure or process exit removes every registration owned by that extension.
  • A tool call fails promptly if its owning extension is unavailable at dispatch or exits while the call is pending.

Actual behavior

  • The failed extension's tool remained available after its process exited.
  • The call did not reach the handler or report an error.

Related issues

#4590 reports a different session.resume failure involving hook-processor disposal during extension reconnects.

#3856 reports the inverse symptom: Extension tools disappear after a resume.

Activity

  1. added
    area:pluginsPlugin system, marketplace, hooks, skills, extensions, and custom agents
    area:sessionsSession management, resume, history, session picker, and session state
    area:toolsBuilt-in tools: file editing, shell, search, LSP, git, and tool call behavior
    on Aug 31, 2026
  2. aurokin commented on Sep 7, 2026

    @aurokin

    Related but distinct attachment fix: github/copilot-agent-runtime#18281 merged on September 7. It fixes github/copilot-agent-runtime#18356, where a non-interactive connection attaching to the same session could disrupt ask_user responder ownership and leave a user-input request pending.

    That PR does not fix this issue's external-tool registration cleanup, ownerless external-tool dispatch, or large-history attachment failure. Those were outside its scope, so this issue should remain open. The separate proposed attachment/external-tool fix, github/copilot-agent-runtime#18059, was closed without merging.

    This report's reproduction has not been rerun against the merged user-input fix, and a released version containing that fix has not been confirmed.

  3. patniko commented on Oct 26, 2026

    @patniko
    Collaborator

    Thanks for the report. We've shipped fixes in this area since this was filed (most recently in Copilot CLI v1.0.85), and we believe this may now be resolved. Could you retest on the latest release (Copilot CLI v1.0.103) and let us know whether you still see it? If we don't hear back in a couple of weeks, we may close this issue — you can always reopen it.

    Triaged as part of a Copilot SDK backlog review, with help from GitHub Copilot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:pluginsPlugin system, marketplace, hooks, skills, extensions, and custom agentsarea:sessionsSession management, resume, history, session picker, and session statearea:toolsBuilt-in tools: file editing, shell, search, LSP, git, and tool call behaviormore-info-needed

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions