Skip to content

fix: defer install prompt until after manager timeout to prevent false positives#1496

Merged
eleanorjboyd merged 2 commits intomainfrom
fix/case-insensitive-manager-id-matching
May 1, 2026
Merged

fix: defer install prompt until after manager timeout to prevent false positives#1496
eleanorjboyd merged 2 commits intomainfrom
fix/case-insensitive-manager-id-matching

Conversation

@edvilme
Copy link
Copy Markdown
Contributor

@edvilme edvilme commented Apr 29, 2026

Fix: Only show install prompt after manager timeout, not during startup

Fixes #1465

Problem

Users consistently see a false "Do you want to install extension ms-python.python to enable ms-python.python:venv support" prompt despite having the extension installed. This happens across Windows/Linux, persists despite reinstalls, and is a race condition during startup.

Root Cause

The race condition:

  • A) The extension host is loading all extensions (takes variable time)
  • B) Our code calls checkExtension() which queries getExtension() after just 1 tick (setImmediate)

B can execute BEFORE A finishes — getExtension() returns undefined even though the extension IS installed, triggering a false install prompt.

The Fix

Separate the two concerns:

  1. checkExtension() — fires early, but only does activation (harmless if it sees stale state — worst case it's a no-op). Never prompts to install.
  2. promptInstallExtensionIfMissing() — fires ONLY after the 30s timeout in withManagerTimeout(), when getExtension() is guaranteed to be reliable.

The install prompt can no longer race against extension host initialization because it's gated behind the same 30s timeout that already exists for "manager didn't register."

Flow (annotated)

// BEFORE (broken):
waitForEnvManagerId(["ms-python.python:venv"])
  ├─ checkExtension()
  │     └─ setImmediate (1ms) → getExtension() → undefined (host still loading!)
  │           └─ ❌ showErrorMessage("Do you want to install...?")  // FALSE PROMPT
  └─ setTimeout(30s) → ...

// AFTER (fixed):
waitForEnvManagerId(["ms-python.python:venv"])
  ├─ checkExtension()
  │     └─ setImmediate (1ms) → getExtension()
  │           └─ ✅ Only activates if found inactive. Never prompts.
  └─ withManagerTimeout(30s)
        ├─ Manager registers at ~200ms → deferred resolves → timer cleared → done ✅
        └─ 30s expire → promptInstallExtensionIfMissing()
              ├─ getExtension() → found? → log warning, NO prompt ✅
              └─ getExtension() → not found? → show install prompt ✅ (genuinely missing)

Changes

  • withManagerTimeout: Now calls promptInstallExtensionIfMissing() on timeout expiry
  • promptInstallExtensionIfMissing (new): Checks getExtension() after 30s; only prompts if extension truly missing; logs warning if installed but manager never registered
  • checkExtension: Simplified to only attempt activation — no install prompts
  • Added 5 regression tests covering: timeout with/without extension installed, manager registering before timeout, pre-registered managers, package manager wait

@edvilme edvilme self-assigned this Apr 29, 2026
@edvilme edvilme added the bug Issue identified by VS Code Team member as probable bug label Apr 29, 2026
@edvilme edvilme force-pushed the fix/case-insensitive-manager-id-matching branch 3 times, most recently from f2201d0 to 6da90bb Compare April 29, 2026 20:47
Previously, checkExtension would show the 'install extension' prompt
inside a setImmediate callback — just one tick after startup. If the
extension host hadn't fully initialized yet, getExtension() could return
undefined even for installed extensions, causing a false prompt.

Now the install prompt is only shown after the full 30-second timeout
expires AND getExtension() still returns undefined. This gives the
extension host ample time to initialize. Additionally, if the extension
IS installed but the manager never registered (activation failure), we
log a warning instead of prompting to install.

Changes:
- Move install prompt logic from checkExtension into
  promptInstallExtensionIfMissing, called only after timeout
- checkExtension now only attempts activation (no install prompts)
- Add guard: if extension is installed but manager never registered,
  log a warning instead of prompting to install
- Add 5 regression tests for the race condition scenarios
- Add _resetManagerReadyForTesting() helper for test isolation

Fixes #1465

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@edvilme edvilme force-pushed the fix/case-insensitive-manager-id-matching branch from 6da90bb to 6857c59 Compare April 29, 2026 20:53
@edvilme edvilme marked this pull request as draft April 29, 2026 20:58
@edvilme edvilme changed the title fix: use case-insensitive manager ID matching to prevent false install prompts fix: defer install prompt until after manager timeout to prevent false positives Apr 29, 2026
@edvilme edvilme marked this pull request as ready for review April 30, 2026 00:21
@eleanorjboyd eleanorjboyd requested a review from Copilot April 30, 2026 14:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a startup race where getExtension() can temporarily return undefined while the VS Code extension host is still initializing, causing a false “install ms-python.python” prompt even when the extension is installed.

Changes:

  • Defers the install prompt until the manager registration timeout expires, preventing false positives during startup.
  • Refactors checkExtension() to only perform best-effort activation (no prompting).
  • Adds regression unit tests covering timeout vs. successful registration scenarios for both env and package managers.
Show a summary per file
File Description
src/features/common/managerReady.ts Moves install prompting behind the manager timeout and simplifies early extension checks to activation-only.
src/test/features/common/managerReady.unit.test.ts Adds unit tests validating the deferred prompt behavior and race-condition scenarios.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 4

Comment thread src/features/common/managerReady.ts Outdated
Comment thread src/features/common/managerReady.ts
Comment thread src/features/common/managerReady.ts Outdated
Comment thread src/features/common/managerReady.ts
- Wrap promptInstallExtensionIfMissing with void/.catch for unhandled rejections
- Attempt activation post-timeout when extension is installed but inactive
- Fix question punctuation (period → question mark)
- Deduplicate install prompts per extension ID per session via prompted Set

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

@edvilme edvilme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 4 review comments addressed in commit 2151164:

  1. Unhandled promise rejection - Wrapped with void ... .catch(traceError)
  2. Activate inactive extension post-timeout - Now attempts ext.activate() when installed but inactive
  3. Punctuation - Changed period to question mark
  4. Deduplicate install prompts - Added module-level prompted Set keyed by extId

@eleanorjboyd eleanorjboyd merged commit 9fbeb75 into main May 1, 2026
84 checks passed
@eleanorjboyd eleanorjboyd deleted the fix/case-insensitive-manager-id-matching branch May 1, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Issue identified by VS Code Team member as probable bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consistently get "Do you want to install extension ms-python.python to enable ms-python.python:venv support."

3 participants