Skip to content

Terminating old extension host agent" SIGKILLs in-flight postCreateCommand (exit 137) when a second window attaches #11610

@vanackere

Description

@vanackere

Environment

  • VS Code: 1.117.0 (commit 10c8e557c8b9f9ed0a87f61f1c9a44bde731c409, 2026-04-21)
  • Dev Containers extension: v0.454.0
  • Host OS: Ubuntu 25.10, Linux 6.17.0-22-generic
  • Remote: Debian bookworm-slim devcontainer, docker-compose backed
  • Docker 29.1.3 / Compose v2.40.2

Summary

When a second VS Code window attaches to a devcontainer while the first window's postCreateCommand is still running, the second window's "Terminating old extension host agent" logic runs kill -9 -$pgrp on every /proc//cmd match of the .vscode-server/bin// regex. In a postCreate that shells out to code --install-extension (a common pattern), that kill takes down the whole script via pgid inheritance and the user sees:

postCreateCommand from devcontainer.json failed with exit code 137.
Skipping any further user-provided commands.

Root cause

dist/extension/extension.js (around the Terminating old extension host agent. string). Rewritten from the minified form:

// 1) Collect every process in the container's mount namespace whose
// cmd path matches the vscode-server/bin regex.
const candidates = procRecords.filter(p =>
p.mntNS === containerMntNS && agentPathRx.test(p.cmd)
);

// 2) Partition by cwd: "current" (cwd is the server install dir) vs "others".
const {current, others} = candidates.reduce((acc, p) => {
const isCurrent =
(p.cwd === canonicalInstallDir ||
p.cwd === serverInstallDir ||
p.cwd === alternateInstallDir)
&& agentPathRx.test(p.cmd);
acc[isCurrent ? 'current' : 'others'].push(p);
return acc;
}, {current: [], others: []});

// 3) SIGKILL the entire PGID of every "other" match.
if (shouldTerminateOthers && others.length) {
output.write('Terminating old extension host agent.\r\n');
try {
await remoteExec(
kill -9 ${others.map(p => p.pgrp ? -${p.pgrp} : p.pid).join(' ')}
);
} catch {}
}

A postCreateCommand that runs code-server --install-extension X execs node /home/dev/.vscode-server/bin//out/server-main.js --install-extension X.
That node process:

  • matches agentPathRx (it is literally a vscode-server/bin/*/node),
  • has cwd = /workdir (the script's cwd), not the server install dir → falls in others,
  • inherits the script's pgid.

kill -9 - therefore hits the postCreate shell, not just the stale agent the scan was targeting.

Steps to reproduce

  1. Devcontainer with a postCreateCommand that iterates code --install-extension for a handful of extensions (a natural auto-provision pattern).
  2. Two windows end up attached to the same devcontainer during the postCreate window. We do not open two ourselves — installing eamodio.gitlens during the postCreate reliably triggers a second window via its install-time walkthrough/URI dispatch.
    window.restoreWindows=all, a user-opened second window, or any other vscode://-URI-dispatching extension should produce the same effect.
  3. postCreateCommand failed with exit code 137 appears in the Dev Containers output pane within milliseconds of the second window's "Terminating old extension host agent." log line.

Evidence

From a reproduced session (only clock-relevant lines):

[window2] 13:00:57.402 scan /proc (pid/cwd/mnt/stat)
[window2] 13:00:57.671 Terminating old extension host agent.
[window2] 13:00:57.680 kill -9 -381
[window2] 13:00:57.688 (8 ms) kill -9 -381
[window1] 13:00:57.689 Stop (41332 ms): /bin/sh -c /workdir/.devcontainer/install-extensions.sh -postcreate
[window1] 13:00:57.689 postCreateCommand from devcontainer.json failed with exit code 137.

Expected behavior

The "old agent" scan should not kill transient node CLI subcommands (--install-extension, --list-extensions, --uninstall-extension, --version), and
should not take down process groups it didn't spawn.

In order of increasing robustness, any of:

  1. Filter by argv. Before classifying a vscode-server/bin/*/node process as an agent, inspect its argv. Only --start-server (or the equivalent
    long-running mode) is an actual agent. Bare --install-extension/--list-extensions/etc. are one-shot CLIs and should be ignored by the scan.
  2. Kill by pid, not pgid. kill -9 p.pid (drop the - for pgid) targets only the matched process rather than every sibling that happens to share a pgid.
  3. Track agents by spawn-time PID (or cgroup / sessionId marker) rather than by /proc scan. Only kill what this extension recognizes it previously spawned.

(1) is the smallest safe fix. (3) is what the scan is trying to approximate.

Additional note

The 2-windows-on-same-container precondition is not rare: any extension whose install path dispatches a vscode:// URI or workbench.action.openWalkthrough during its activation (GitLens is the case we hit) will cause a second window to be created while the host window is mid-remote-resolution. So this bug is reachable from a plain single-window Rebuild Container command with no user intent to have two windows — which is how we originally hit it (and why it took so long for us to diagnose)

Metadata

Metadata

Assignees

Labels

containersIssue in vscode-remote containers

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions