Describe the bug
After I resumed a long-lived session, an extension failed during session.resume before it reported ready. The CLI continued to expose the extension's custom tool after the extension process exited.
The call never reached the handler and returned no error. It remained pending for 9 minutes 38 seconds until I aborted it.
The startup failure was:
Request session.resume failed with message: Invalid string length
peer-closed-before-ready
exit code=1 disposition=startup-failure
The tool's successful path would create a target file. No target appeared. The extension process had already exited, so the stale registration had no live handler.
Environment
GitHub Copilot CLI 1.0.82
Linux x86_64
Relevant extension shape
The affected extension registered a custom tool through joinSession(). This reduced example shows the same lifecycle shape.
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";
},
},
],
});
Steps from the failing run
- I stored the extension under
~/.copilot/session-state/<session-id>/extensions/stale-tool-probe/extension.mjs.
- I resumed the session.
- The extension produced the quoted startup failure and exited before reporting ready.
- I asked the agent to invoke
stale_tool_probe.
- The CLI still offered the tool, and the call remained pending instead of returning an error about the exited extension.
The trigger may depend on session size or resume history. The failing session's events.jsonl file was about 1.2 GB. I do not yet have a smaller deterministic reproducer.
A separate hook-and-tool extension using the same SDK joined a fresh small session, handled tool calls, and survived resume. Extensions and joinSession() therefore work generally on this host.
Expected behavior
- The CLI exposes a tool registration only after its owning extension reports ready.
- A startup failure removes every provisional registration owned by that process.
- A call to a tool whose extension has exited fails promptly and names that extension.
- Reloading extensions removes registrations whose owning process has exited.
- The CLI reports the
session.resume failure and any later stale-tool call as separate errors.
Actual behavior
- The extension failed before ready and exited.
- Its tool stayed visible.
- The stale call never reached the handler.
- The stale call returned no error within 9 minutes 38 seconds.
- I recovered only by aborting the call.
Additional context
I inspected the published SDK's joinSession() implementation. Its session.resume request contains extension registration options rather than session history. A successful response contains workspace, capability, and canvas metadata.
The large session file correlates with the failure, but the evidence does not establish it as the cause. The failure appears in the runtime's session resume or extension attachment path, but I have not isolated the component.
Potentially related issues
Issue 4590 reports a different session.resume failure and hook-processor disposal during extension reconnects.
Issue 3856 reports extension tools disappearing when a resume operation creates separate contexts.
The current report covers the opposite tool lifecycle outcome: The CLI keeps a tool callable after its extension fails before reporting ready.
Describe the bug
After I resumed a long-lived session, an extension failed during
session.resumebefore it reported ready. The CLI continued to expose the extension's custom tool after the extension process exited.The call never reached the handler and returned no error. It remained pending for 9 minutes 38 seconds until I aborted it.
The startup failure was:
The tool's successful path would create a target file. No target appeared. The extension process had already exited, so the stale registration had no live handler.
Environment
Relevant extension shape
The affected extension registered a custom tool through
joinSession(). This reduced example shows the same lifecycle shape.Steps from the failing run
~/.copilot/session-state/<session-id>/extensions/stale-tool-probe/extension.mjs.stale_tool_probe.The trigger may depend on session size or resume history. The failing session's
events.jsonlfile was about 1.2 GB. I do not yet have a smaller deterministic reproducer.A separate hook-and-tool extension using the same SDK joined a fresh small session, handled tool calls, and survived resume. Extensions and
joinSession()therefore work generally on this host.Expected behavior
session.resumefailure and any later stale-tool call as separate errors.Actual behavior
Additional context
I inspected the published SDK's
joinSession()implementation. Itssession.resumerequest contains extension registration options rather than session history. A successful response contains workspace, capability, and canvas metadata.The large session file correlates with the failure, but the evidence does not establish it as the cause. The failure appears in the runtime's session resume or extension attachment path, but I have not isolated the component.
Potentially related issues
Issue 4590 reports a different
session.resumefailure and hook-processor disposal during extension reconnects.Issue 3856 reports extension tools disappearing when a resume operation creates separate contexts.
The current report covers the opposite tool lifecycle outcome: The CLI keeps a tool callable after its extension fails before reporting ready.