microsoft / microsoft/vscode-remote-release

vs code fails to connect to remote ssh after update

Open
#11,778 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Dockerfile
Stars
4.2k
Forks
470
Avg merge
1d 1h
Merged PRs (30d)
1

Description

Remote-SSH: askpass IPC socket path exceeds macOS sun_path limit, causing an endless reconnect loop

Environment

  • VS Code: 1.128.1
  • Remote-SSH: 0.122.0
  • Local OS: macOS 26.5.2 (build 25F84), arm64
  • Remote OS: Amazon Linux 2, kernel 5.10.260, x86_64
  • Connection: SSH via a ProxyCommand helper

Summary

getIPCHandlePath() builds the askpass IPC socket path from os.tmpdir() plus a
40-hex-character random suffix. On macOS the resulting path is 113 characters, which
exceeds the 104-byte sun_path limit for AF_UNIX sockets. listen() fails with
EINVAL, the socket is never created, and the connection then falls into an
unbounded reconnect loop.

This is deterministic on macOS, not environment-specific. Apple's per-user temporary
directory has a fixed-width format:

/var/folders/<2>/<30>/T                      = 48 chars
/vscode-ssh-askpass-<40 hex>.sock            = 65 chars
                                        total = 113 chars   (limit: 104)

The only call site is name = "askpass", and 20 random bytes is the default, so every
macOS user produces the same 113-character path.

Steps to reproduce

  1. On macOS, connect to any SSH host with Remote-SSH.
  2. Open the Remote-SSH output channel.

Expected

The askpass IPC socket is created and the connection remains stable.

Actual

The socket is never created and the connection loops. Relevant log excerpt:

stderr> local-server-3> listen EINVAL: invalid argument /var/folders/qt/<redacted>/T/vscode-ssh-askpass-<40hex>.sock
Server delay-shutdown request failed: connect EINVAL /var/folders/qt/<redacted>/T/vscode-ssh-askpass-<40hex>.sock - Local (undefined:undefined)
Server delay-shutdown request failed: connect EINVAL ... (repeats)
Exec server for ssh-remote+<host> closed (gracefully)
> local-server-3> Timed out
SSH Resolver called for "ssh-remote+<host>", attempt 8, (Reconnection)

The failure chain:

  1. listen() on the askpass socket fails with EINVAL (path too long).
  2. Because the socket does not exist, every delay-shutdown request fails with EINVAL.
  3. Without a successful delay-shutdown, the local server times out after roughly five seconds.
  4. The resolver reconnects, and the cycle repeats indefinitely.

The SSH transport itself is healthy throughout: authentication succeeds, the remote
server starts and reports a listening port, and the tunnel is established each time.
Only the local askpass socket fails.

Offending code

out/extension.js (and a duplicate copy in out/resolver.js):

getIPCHandlePath = async function (e, t = 20) {
  const n = r.promisify(o.randomBytes);
  return function (e, t) {
    return "win32" === process.platform
      ? `\\\\.\\pipe\\vscode-ssh-${e}-${t}-sock`
      : process.env.XDG_RUNTIME_DIR
        ? i.join(process.env.XDG_RUNTIME_DIR, `vscode-ssh-${e}-${t}.sock`)
        : i.join(s.tmpdir(), `vscode-ssh-${e}-${t}.sock`);   // <-- too long on darwin
  }(e, (await n(t)).toString("hex"));
};

Note that XDG_RUNTIME_DIR is not set on macOS, so the os.tmpdir() branch is always taken.

Confirmed workaround

Replacing the base directory with /tmp in out/extension.js reduces the path to 69
characters and fully resolves the issue:

i.join("/tmp", `vscode-ssh-${e}-${t}.sock`)

After this change: the socket is created successfully, no EINVAL appears, the resolver
succeeds on attempt 1, and the connection is stable.

Worth noting for whoever picks this up: the utility is duplicated into both
out/extension.js and out/resolver.js by the bundler. Patching only resolver.js
has no effect, since package.json declares main: ./out/extension.

Suggested fixes

Either of these resolves it:

  1. Reduce the random suffix on non-Windows platforms. 8 bytes (16 hex characters) yields
    an 89-character path, which fits. 20 bytes of entropy is more than a short-lived local
    IPC socket needs.
  2. Fall back to /tmp on darwin when os.tmpdir() would produce a path over the limit.

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.

Research direction

Start by tracing getIPCHandlePath in out/extension.js and its duplicate in out/resolver.js, then check package.json to confirm the extension entry point. Reproduce the macOS Remote-SSH connection and verify the askpass socket path and listen result in the output log. Done means the socket is created within the AF_UNIX path limit and the resolver connects without repeated retries.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js, vscode
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.