@@ -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 , PythonLocator } 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,33 @@ 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 ( PythonLocator . windowsRuntimeMissing ) ;
445+ } else if ( isWindows ( ) ) {
446+ this . outputChannel . error ( PythonLocator . windowsStartupFailed ) ;
447+ }
448+
449+ // Show notification to user
450+ const selection = await showWarningMessage (
451+ PythonLocator . startupFailedNotification ,
452+ Common . openOutputPanel ,
453+ Common . doNotShowAgain ,
454+ ) ;
455+
456+ if ( selection === Common . openOutputPanel ) {
457+ await executeCommand ( Commands . ViewOutput ) ;
458+ } else if ( selection === Common . doNotShowAgain ) {
459+ await this . suppressErrorNotification . set ( true ) ;
460+ }
461+ }
400462}
401463
402464type ConfigurationOptions = {
@@ -461,7 +523,7 @@ export function getNativePythonFinder(context?: IExtensionContext): NativePython
461523 }
462524 if ( ! _finder ) {
463525 const cacheDirectory = context ? getCacheDirectory ( context ) : undefined ;
464- _finder = new NativePythonFinderImpl ( cacheDirectory ) ;
526+ _finder = new NativePythonFinderImpl ( cacheDirectory , context ) ;
465527 if ( context ) {
466528 context . subscriptions . push ( _finder ) ;
467529 }
0 commit comments