@@ -15,18 +15,24 @@ import { noop } from '../../../../common/utils/misc';
1515import { getConfiguration , getWorkspaceFolderPaths , isTrusted } from '../../../../common/vscodeApis/workspaceApis' ;
1616import { CONDAPATH_SETTING_KEY } from '../../../common/environmentManagers/conda' ;
1717import { VENVFOLDERS_SETTING_KEY , VENVPATH_SETTING_KEY } from '../lowLevel/customVirtualEnvLocator' ;
18- import { createLogOutputChannel } from '../../../../common/vscodeApis/windowApis' ;
18+ import { createLogOutputChannel , showWarningMessage } from '../../../../common/vscodeApis/windowApis' ;
1919import { sendNativeTelemetry , NativePythonTelemetry } from './nativePythonTelemetry' ;
2020import { NativePythonEnvironmentKind } from './nativePythonUtils' ;
2121import type { IExtensionContext } from '../../../../common/types' ;
2222import { StopWatch } from '../../../../common/utils/stopWatch' ;
2323import { untildify } from '../../../../common/helpers' ;
2424import { traceError } from '../../../../logging' ;
25+ import { Common } from '../../../../common/utils/localize' ;
26+ import { Commands } from '../../../../common/constants' ;
27+ import { executeCommand } from '../../../../common/vscodeApis/commandApis' ;
28+ import { getGlobalStorage , IPersistentStorage } from '../../../../common/persistentState' ;
2529
2630const PYTHON_ENV_TOOLS_PATH = isWindows ( )
2731 ? path . join ( EXTENSION_ROOT_DIR , 'python-env-tools' , 'bin' , 'pet.exe' )
2832 : path . join ( EXTENSION_ROOT_DIR , 'python-env-tools' , 'bin' , 'pet' ) ;
2933
34+ const DONT_SHOW_SPAWN_ERROR_AGAIN = 'DONT_SHOW_NATIVE_FINDER_SPAWN_ERROR_AGAIN' ;
35+
3036export interface NativeEnvInfo {
3137 displayName ?: string ;
3238 name ?: string ;
@@ -106,8 +112,13 @@ class NativePythonFinderImpl extends DisposableBase implements NativePythonFinde
106112 timeToRefresh : 0 ,
107113 } ;
108114
109- constructor ( private readonly cacheDirectory ?: Uri ) {
115+ private readonly suppressErrorNotification : IPersistentStorage < boolean > ;
116+
117+ constructor ( private readonly cacheDirectory ?: Uri , private readonly context ?: IExtensionContext ) {
110118 super ( ) ;
119+ this . suppressErrorNotification = this . context
120+ ? getGlobalStorage < boolean > ( this . context , DONT_SHOW_SPAWN_ERROR_AGAIN , false )
121+ : ( { get : ( ) => false , set : async ( ) => { } } as IPersistentStorage < boolean > ) ;
111122 this . connection = this . start ( ) ;
112123 void this . configure ( ) ;
113124 this . firstRefreshResults = this . refreshFirstTime ( ) ;
@@ -212,6 +223,30 @@ class NativePythonFinderImpl extends DisposableBase implements NativePythonFinde
212223 proc . stderr . on ( 'data' , ( data ) => this . outputChannel . error ( data . toString ( ) ) ) ;
213224 writable . pipe ( proc . stdin , { end : false } ) ;
214225
226+ // Handle spawn errors (e.g., missing DLLs on Windows)
227+ proc . on ( 'error' , ( error ) => {
228+ this . outputChannel . error ( `Python Locator process error: ${ error . message } ` ) ;
229+ this . outputChannel . error ( `Error details: ${ JSON . stringify ( error ) } ` ) ;
230+ this . handleSpawnError ( error . message ) ;
231+ } ) ;
232+
233+ // Handle immediate exits with error codes
234+ let hasStarted = false ;
235+ setTimeout ( ( ) => {
236+ hasStarted = true ;
237+ } , 1000 ) ;
238+
239+ proc . on ( 'exit' , ( code , signal ) => {
240+ if ( ! hasStarted && code !== null && code !== 0 ) {
241+ const errorMessage = `Python Locator process exited immediately with code ${ code } ` ;
242+ this . outputChannel . error ( errorMessage ) ;
243+ if ( signal ) {
244+ this . outputChannel . error ( `Exit signal: ${ signal } ` ) ;
245+ }
246+ this . handleSpawnError ( errorMessage ) ;
247+ }
248+ } ) ;
249+
215250 disposables . push ( {
216251 dispose : ( ) => {
217252 try {
@@ -397,6 +432,40 @@ class NativePythonFinderImpl extends DisposableBase implements NativePythonFinde
397432 async getCondaInfo ( ) : Promise < NativeCondaInfo > {
398433 return this . connection . sendRequest < NativeCondaInfo > ( 'condaInfo' ) ;
399434 }
435+
436+ private async handleSpawnError ( errorMessage : string ) : Promise < void > {
437+ // Check if user has chosen to not see this error again
438+ if ( this . suppressErrorNotification . get ( ) ) {
439+ return ;
440+ }
441+
442+ // Check for Windows runtime DLL issues
443+ if ( isWindows ( ) && errorMessage . toLowerCase ( ) . includes ( 'vcruntime' ) ) {
444+ this . outputChannel . error (
445+ 'Missing Windows runtime dependencies detected. ' +
446+ 'The Python Locator requires the Microsoft Visual C++ Redistributable. ' +
447+ 'This is often missing on clean Windows installations.' ,
448+ ) ;
449+ } else if ( isWindows ( ) ) {
450+ this . outputChannel . error (
451+ 'Python Locator failed to start on Windows. ' +
452+ 'This might be due to missing system dependencies such as the Microsoft Visual C++ Redistributable.' ,
453+ ) ;
454+ }
455+
456+ // Show notification to user
457+ const selection = await showWarningMessage (
458+ 'Python Locator failed to start. Python environment discovery may not work correctly.' ,
459+ Common . openOutputPanel ,
460+ Common . doNotShowAgain ,
461+ ) ;
462+
463+ if ( selection === Common . openOutputPanel ) {
464+ await executeCommand ( Commands . ViewOutput ) ;
465+ } else if ( selection === Common . doNotShowAgain ) {
466+ await this . suppressErrorNotification . set ( true ) ;
467+ }
468+ }
400469}
401470
402471type ConfigurationOptions = {
@@ -461,7 +530,7 @@ export function getNativePythonFinder(context?: IExtensionContext): NativePython
461530 }
462531 if ( ! _finder ) {
463532 const cacheDirectory = context ? getCacheDirectory ( context ) : undefined ;
464- _finder = new NativePythonFinderImpl ( cacheDirectory ) ;
533+ _finder = new NativePythonFinderImpl ( cacheDirectory , context ) ;
465534 if ( context ) {
466535 context . subscriptions . push ( _finder ) ;
467536 }
0 commit comments