Skip to content

pytest runs always take an extra ~5 s on macOS since 2026.8.0 (result pipe close never detected, drain timeout always hit) #26217

Description

@bnarum

Type: Bug

Full disclosure: I found the bug and could reproduce. The diagnostics below are made by Claude Code.

Environment

  • VS Code: 1.141.0 (arm64)
  • Python extension: 2026.8.0 (regression; 2026.2.2026021801 is not affected)
  • Python Environments extension: 1.38.0
  • OS: macOS 26.7.1 (Apple Silicon)
  • Python 3.13.16, pytest 9.1.1, local venv

Summary

Every pytest run started from the Test Explorer takes about 5 seconds longer than pytest itself. The test is marked as passed almost immediately, but the run stays in progress, and "Finished running tests!" only appears about 5 seconds later. During that time a new run can't start ("Test execution already in progress, not starting a new one.").

This happens even for a single trivial test: pytest reports 1 passed in 0.02s, the Python process exits after about 0.6 s, and the Test Explorer reports 5.6 s.

Steps to reproduce

  1. On macOS, open a folder that contains:
    # test_trivial.py
    def test_trivial() -> None:
        assert True
  2. Enable pytest ("python.testing.pytestEnabled": true).
  3. Run test_trivial from the Test Explorer.

Expected: the run ends as soon as the pytest process exits (well under 1 s, as with 2026.2).
Actual: the run ends about 5 s after pytest exits. The duration in the Test Explorer is about 5 s plus the pytest time.

Analysis

In 2026.8.0, PytestTestExecutionAdapter.runTests ends with:

finally { await awaitDeferredWithTimeout(deferredTillServerClose, RESULT_PIPE_DRAIN_TIMEOUT_MS) } // 5e3

That deferred is resolved only by startRunResultNamedPipe, through the reader's onClose. In 2026.2 the deferred was also resolved when the cancellation token was cancelled (n.onCancellationRequested(() => s.dispose())), which runTestsNew triggers when the subprocess closes. 2026.8.0 removed that path, so the run now ends only when onClose fires.

On macOS onClose never fires. On non-Windows platforms createReaderPipe does mkfifo, open(O_RDONLY | O_NONBLOCK) and wraps the fd in new net.Socket({ fd }). When the writer (the vscode_pytest plugin) writes and its process exits, that socket receives the data but never emits end or close. The wait therefore always runs to the 5000 ms timeout.

Standalone reproduction of the reader's behaviour (Node 26 on macOS; I could not test VS Code's bundled Electron/Node directly):

// fifo_probe.mjs - usage: node fifo_probe.mjs /path/to/python
import { execFileSync, spawn } from "node:child_process";
import { promises as fsp, constants as C, rmSync } from "node:fs";
import net from "node:net";

const p = `${process.env.TMPDIR}/probe_fifo_${process.pid}`;
rmSync(p, { force: true });
execFileSync("mkfifo", [p]);
const t0 = Date.now();
const log = (m) => console.log(`[+${((Date.now() - t0) / 1000).toFixed(2)}s] ${m}`);

const fh = await fsp.open(p, C.O_RDONLY | C.O_NONBLOCK);
const s = new net.Socket({ fd: fh.fd });
for (const ev of ["data", "end", "close", "error"]) s.on(ev, () => log(`socket ${ev}`));

const writer = `import time\nf=open(${JSON.stringify(p)},"wb"); f.write(b"content-length: 2\\n\\n{}"); f.flush(); time.sleep(0.3)`;
spawn(process.argv[2], ["-c", writer], { stdio: "inherit" }).on("exit", () => log("writer exited"));
setTimeout(() => { log("gave up after 6s"); process.exit(0); }, 6000);

Output:

[+0.02s] socket data
[+0.33s] writer exited
[+6.00s] gave up after 6s

No end or close is emitted after the writer exits.

Suggested fix

Resolve the drain deferred once the subprocess has exited and the pipe has had a chance to flush (as 2026.2 did through the cancellation token). Alternatively, end the wait when the plugin's final message for the run arrives, rather than relying on EOF from a non-blocking FIFO socket, which doesn't arrive on macOS.

Workaround

Install Python extension 2026.2.x ("Install Specific Version...") and disable auto-update for it.

Extension version: 2026.8.0
VS Code version: Code 1.141.0 (Universal) (2a59476c9bfcb90b3ddc372c36762471b7dfad1c, 2026-10-06T10:15:52+02:00)
OS version: Darwin arm64 25.6.0
Modes:

  • Python version (& distribution if applicable, e.g. Anaconda): 3.13.16
  • Type of virtual environment used (e.g. conda, venv, virtualenv, etc.): Poetry
  • Value of the python.languageServer setting: Pylance
User Settings


venvPath: "<placeholder>"

languageServer: "Pylance"

testing
• pytestArgs: "<placeholder>"
• pytestEnabled: true
• autoTestDiscoverOnSavePattern: "<placeholder>"

terminal
• activateEnvironment: false

experiments
• enabled: false

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions