microsoft / microsoft/vscode-remote-release
Remote SSH: server shuts down after reconnect due to hardcoded 5-minute extension host timer triggered by IPC failure
@alexdima is already working on this.
Since Jul 3, 2026.
- Dominant language
- Dockerfile
- Stars
- 4.2k
- Forks
- 470
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 1
Description
Environment
Client (local machine):
- OS: Ubuntu 22.04
- Kernel: 6.8.0-124-generic (x64)
- VS Code: 1.127.0 (commit
4fe60c8b, x64)
Server (remote machine):
- OS: Ubuntu 22.04.5 LTS (Jammy Jellyfish)
- Kernel: 6.8.0-1060-aws (x86_64, AWS EC2 instance)
- VS Code Server: reproduced on 1.127.0 (
4fe60c8b) and 1.128.0 (fc3def67) - Node.js: v24.14.1
- Connection type: Remote - SSH
Steps to Reproduce
- Connect to a remote Linux machine via Remote SSH
- Work normally for 30+ minutes (extension host IPC pipe is active)
- Disconnect for 60–90 minutes (sleep laptop / lose network)
- Reconnect from VS Code client (client may have restarted, generating a new session token)
Expected Behavior
The server spawns a fresh extension host if the old IPC pipe is broken, and the
session resumes normally.
Actual Behavior
The server shuts down completely within ~10 minutes of reconnection. The user must
manually trigger a new connection, which spins up a fresh server. This has occurred
reproducibly 6+ times over one week.
Root Cause
There are two independent 5-minute timers that can each kill the server — either
one is sufficient to trigger a full shutdown.
Timer 1 — Extension host internal timer (extensionHostProcess.js)
When the extension host receives a new client connection (IPC message type===2),
it starts two countdown timers:
let r = fZ("VSCODE_RECONNECTION_GRACE_TIME", 108e5) // 3 hours
let s = r > 0 ? Math.min(3e5, r) : 0 // hardcoded 5-min cap
let a = new qh(() => ta("renderer disconnected for too long (1)"), r)
let l = new qh(() => ta("renderer disconnected for too long (2)"), s)
The 3e5 cap (300,000 ms = 5 minutes) is hardcoded. Setting
VSCODE_RECONNECTION_GRACE_TIME does not change it — the second timer is always
capped at 5 minutes regardless. If the renderer does not fully re-establish its
connection within 5 minutes, the extension host calls process.exit(0).
Timer 2 — ManagementConnection short grace time (server-main.js)
In server-main.js, the ManagementConnection class has a second 5-minute timer:
let s = 3e5; // hardcoded 5 minutes
this._reconnectionShortGraceTime = i > 0 ? Math.min(s, i) : 0
When a new client connects to an existing session (shortenReconnectionGraceTimeIfNecessary()
is called), this timer starts. After 5 minutes it calls _cleanResources(), which
disposes the ManagementConnection and triggers ExtensionHostConnection._cleanResources(),
which calls this._extensionHostProcess.kill() — externally terminating the extension
host process.
This is the timer responsible for the log message:
"Another client has connected, will shorten the wait for reconnection 5m before disposing"
Once the management connection is disposed, the server shuts down 5 minutes later
("all consumers inactive").
Triggering condition: SIGPIPE on reconnect
Both timers are triggered by the underlying SIGPIPE bug: after a long disconnection
(~60–90 min), the extension host closes its IPC handle. When the client reconnects
with a new session token, the server calls process.send() to deliver a new socket
to the old extension host — but the IPC pipe is dead, raising SIGPIPE. The server
does not recover by spawning a fresh extension host. Instead both 5-minute timers
begin their countdown, and whichever fires first triggers a full server shutdown.
Full log sequences (from remoteagent.log, times UTC)
Timer 2 example (ManagementConnection short grace time):
2026-07-09 16:06:26 [error] Error: Unexpected SIGPIPE
2026-07-09 17:03:16 [info] [ManagementConnection] Another client has connected,
will shorten the wait for reconnection 5m before disposing...
2026-07-09 17:08:16 [info] [ExtensionHostConnection] <2950> Extension Host Process
exited with code: 0, signal: null.
2026-07-09 17:08:18 [info] [ExtensionHostConnection] <14423> Extension Host Process
exited with code: 0, signal: null.
2026-07-09 17:13:18 [info] ServerLifetime: all consumers inactive, shutting down
Timer 1 example (extension host internal timer):
2026-07-02 15:47:15 [error] Error: Unexpected SIGPIPE
2026-07-03 00:17:00 [info] [ManagementConnection] Another client has connected,
will shorten the wait for reconnection 5m before disposing...
2026-07-03 00:22:01 [info] [ExtensionHostConnection] <49772> Extension Host Process
exited with code: 0, signal: null.
2026-07-03 00:36:35 [info] ServerLifetime: all consumers inactive, shutting down
Proposed Fixes
Fix 1 (preferred — root cause): When process.send() raises SIGPIPE, spawn a
fresh extension host instead of abandoning the old one. This eliminates the trigger
condition for both timers.
Fix 2 (mitigation — extensionHostProcess.js): Remove the hardcoded 5-minute cap
in the extension host internal timer — change Math.min(3e5, r) to r. This lets
the extension host wait the full configured grace period before giving up.
Fix 3 (mitigation — server-main.js): Remove the hardcoded 5-minute cap in
ManagementConnection._reconnectionShortGraceTime — change let s = 3e5 to let s = i
(where i is the reconnection grace time). This prevents the management connection
from being disposed prematurely when a new client connects.
Fix 4 (client-side): During rapid reconnect cycles caused by brief network drops,
reuse the same reconnection token so the server does not interpret each retry as a new
client arrival triggering the short grace time.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.