Skip to content

Notebook kernel selection hangs after PET resolve burst/timeout; recovery callbacks lose process identity #1932

Open

Description

@lowlandsheperd

Environment data

  • Python Environments: 1.38.0
  • Python extension: 2026.8.0
  • Jupyter: 2026.6.2026071501
  • VS Code: 1.134.0
  • Remote-SSH: Windows desktop client, Linux x64 host, bash, single-folder workspace
  • Conda interpreter: Python 3.7.13, ipykernel 6.9.1
  • Last known working extension version: unknown

Repro Steps

  1. With Python Environments, Python and Jupyter enabled, open a notebook over Remote-SSH.
  2. Select an existing Conda environment from the notebook kernel picker.
  3. The environment row becomes highlighted, but the picker stays open and no kernel starts.
  4. Disable Python Environments and reload the window. Selecting the same environment then works, and its kernel starts in under one second.

This describes the affected session. The initial stall has not been reproduced independently using PET CLI; standalone resolution returned complete metadata promptly.

Expected behavior

Selecting a valid environment should complete or report a recoverable error. Recovery from a PET timeout should keep callbacks associated with the process generation that created them.

Actual behavior / log summary

  • 338 successful resolution messages for the same interpreter in two seconds; 354 in the captured session. These are completion counts, not request-start measurements.
  • A subsequent resolve exceeded the extension's 30-second timeout.
  • Recovery logged a PET restart, 113 Unable to handle messages, SIGTERM, a successful restart, then SIGKILL and another restart.
  • Disabling Python Environments resolved the selection hang without changing the notebook or Python environment.

Only generic diagnostic messages are quoted; local paths, names and timestamps are omitted:

[pet] Resolve request timed out, killing process for restart
[pet] Restarting Python Environment Tools (attempt 1/3, waiting 1000ms)
[pet] Python Environment Tools exited unexpectedly with code null, signal SIGTERM
[pet] Python Environment Tools restarted successfully
[pet] Python Environment Tools exited unexpectedly with code null, signal SIGKILL

Source-level findings

In nativePythonFinder.ts at v1.38.0:

  • resolve() sends each request separately with a 30-second timeout; this method has no same-executable in-flight coalescing. On timeout it kills the shared PET process.
  • killProcess() (line 544) schedules a 500-ms callback that reads this.proc, then immediately clears this.proc. Consequently it loses the original shutdown target. If a replacement has been assigned before that callback runs, the callback targets the replacement.
  • The exit callback in start() (line 688) unconditionally sets finder-wide processExited, without checking whether that child is still current.

These callback behaviors were verified by extracting the actual methods from the installed bundle and executing them with fake ChildProcess objects and controlled timers. No real processes were killed.

A minimal illustration of the shutdown-reference problem:

const signals = [];
const timers = [];
const child = name => ({
  exitCode: null,
  kill: signal => signals.push([name, signal])
});
const finder = { proc: child('old') };
function shutdown() {
  this.proc.kill('SIGTERM');
  timers.push(() => {
    if (this.proc && this.proc.exitCode === null)
      this.proc.kill('SIGKILL');
  });
  this.proc = undefined;
}
shutdown.call(finder);
finder.proc = child('replacement');
timers[0]();
console.log(signals); // old: SIGTERM; replacement: SIGKILL

This illustration deliberately forces replacement-before-callback ordering. The normal first-restart backoff is 1000 ms, longer than the 500-ms timer. It demonstrates unsafe callback lifetime, not that this ordering caused the observed SIGKILL. With no replacement, the callback sends no SIGKILL to an old child that ignores SIGTERM.

Additional context

The origin of the initial stall and repeated calls is unknown. No caller trace or PET I/O trace was captured. The signal source and process identity behind the production SIGKILL are also unconfirmed.

Suggested investigation: identify the repeated-resolution caller, consider per-executable coalescing, capture the exact ChildProcess in shutdown timers, and guard callbacks and request-error recovery by process/connection generation.

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