@@ -256,10 +256,38 @@ export async function runCondaExecutable(
256256 return await _runConda ( conda , args , log , token ) ;
257257}
258258
259- // eslint-disable-next-line @typescript-eslint/no-explicit-any
260- async function getCondaInfo ( ) : Promise < any > {
259+ interface CondaInfo {
260+ envs_dirs : string [ ] ;
261+ }
262+
263+ /**
264+ * Runs `conda info --envs --json` and parses the result.
265+ * Validates the JSON response structure at the parsing boundary.
266+ * @returns Validated CondaInfo object
267+ * @throws Error if conda command fails or returns invalid JSON structure
268+ */
269+ async function getCondaInfo ( ) : Promise < CondaInfo > {
261270 const raw = await runConda ( [ 'info' , '--envs' , '--json' ] ) ;
262- return JSON . parse ( raw ) ;
271+ const parsed = JSON . parse ( raw ) ;
272+
273+ // Validate at the JSON→TypeScript boundary
274+ if ( ! parsed || typeof parsed !== 'object' ) {
275+ traceWarn ( `conda info returned invalid data: ${ typeof parsed } ` ) ;
276+ throw new Error ( `conda info returned invalid data type: ${ typeof parsed } ` ) ;
277+ }
278+
279+ const envsDirs = parsed [ 'envs_dirs' ] ;
280+ if ( envsDirs === undefined || envsDirs === null ) {
281+ traceWarn ( 'conda info envs_dirs is undefined/null' ) ;
282+ return { envs_dirs : [ ] } ;
283+ }
284+ if ( ! Array . isArray ( envsDirs ) ) {
285+ traceWarn ( `conda info envs_dirs is not an array (type: ${ typeof envsDirs } )` ) ;
286+ return { envs_dirs : [ ] } ;
287+ }
288+
289+ traceVerbose ( `conda info returned ${ envsDirs . length } environment directories` ) ;
290+ return { envs_dirs : envsDirs } ;
263291}
264292
265293let prefixes : string [ ] | undefined ;
@@ -269,17 +297,15 @@ export async function getPrefixes(): Promise<string[]> {
269297 }
270298
271299 const state = await getWorkspacePersistentState ( ) ;
272- prefixes = await state . get < string [ ] > ( CONDA_PREFIXES_KEY ) ;
273- if ( prefixes ) {
300+ const storedPrefixes = await state . get < string [ ] > ( CONDA_PREFIXES_KEY ) ;
301+ if ( storedPrefixes && Array . isArray ( storedPrefixes ) ) {
302+ prefixes = storedPrefixes ;
274303 return prefixes ;
275304 }
276305
277306 try {
278307 const data = await getCondaInfo ( ) ;
279- prefixes = Array . isArray ( data [ 'envs_dirs' ] ) ? ( data [ 'envs_dirs' ] as string [ ] ) : [ ] ;
280- if ( prefixes . length === 0 ) {
281- traceWarn ( 'Conda info returned no environment directories (envs_dirs)' ) ;
282- }
308+ prefixes = data . envs_dirs ;
283309 await state . set ( CONDA_PREFIXES_KEY , prefixes ) ;
284310 } catch ( error ) {
285311 traceError ( 'Failed to get conda environment prefixes' , error ) ;
@@ -624,6 +650,11 @@ async function nativeToPythonEnv(
624650 conda : string ,
625651 condaPrefixes : string [ ] ,
626652) : Promise < PythonEnvironment | undefined > {
653+ // Defensive check: Validate NativeEnvInfo object
654+ if ( ! e ) {
655+ traceWarn ( 'nativeToPythonEnv received null/undefined NativeEnvInfo' ) ;
656+ return undefined ;
657+ }
627658 if ( ! ( e . prefix && e . executable && e . version ) ) {
628659 let name = e . name ;
629660 const environment = api . createPythonEnvironmentItem (
@@ -687,20 +718,45 @@ export async function refreshCondaEnvs(
687718 log : LogOutputChannel ,
688719 manager : EnvironmentManager ,
689720) : Promise < PythonEnvironment [ ] > {
690- log . info ( 'Refreshing conda environments' ) ;
691- const data = await nativeFinder . refresh ( hardRefresh ) ;
721+ log . info ( `Refreshing conda environments (hardRefresh=${ hardRefresh } )` ) ;
722+
723+ let data : ( NativeEnvInfo | NativeEnvManagerInfo ) [ ] ;
724+ try {
725+ data = await nativeFinder . refresh ( hardRefresh ) ;
726+ } catch ( error ) {
727+ traceError ( 'Failed to refresh native finder for conda environments' , error ) ;
728+ log . error ( `Failed to refresh native finder: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
729+ return [ ] ;
730+ }
731+
732+ // Ensure data is a valid array before proceeding
733+ if ( ! data || ! Array . isArray ( data ) ) {
734+ traceWarn ( `Native finder returned invalid data: ${ typeof data } , expected array` ) ;
735+ log . warn ( `Native finder returned invalid data type: ${ typeof data } ` ) ;
736+ return [ ] ;
737+ }
738+
739+ traceVerbose ( `Native finder returned ${ data . length } items for conda refresh` ) ;
692740
693741 let conda : string | undefined = undefined ;
694742 try {
695743 conda = await getConda ( ) ;
696- } catch {
744+ } catch ( error ) {
745+ traceVerbose ( `getConda() failed, will try to find conda from native data: ${ error } ` ) ;
697746 conda = undefined ;
698747 }
699748 if ( conda === undefined ) {
700749 const managers = data
701750 . filter ( ( e ) => ! isNativeEnvInfo ( e ) )
702751 . map ( ( e ) => e as NativeEnvManagerInfo )
703752 . filter ( ( e ) => e . tool . toLowerCase ( ) === 'conda' ) ;
753+
754+ if ( managers . length === 0 ) {
755+ traceWarn ( 'No conda manager found in native finder data' ) ;
756+ log . warn ( 'No conda manager found in native finder data' ) ;
757+ return [ ] ;
758+ }
759+
704760 conda = managers [ 0 ] . executable ;
705761 await setConda ( conda ) ;
706762 }
@@ -717,9 +773,21 @@ export async function refreshCondaEnvs(
717773
718774 await Promise . all (
719775 envs . map ( async ( e ) => {
720- const environment = await nativeToPythonEnv ( e , api , manager , log , condaPath , condaPrefixes ) ;
721- if ( environment ) {
722- collection . push ( environment ) ;
776+ try {
777+ const environment = await nativeToPythonEnv ( e , api , manager , log , condaPath , condaPrefixes ) ;
778+ if ( environment ) {
779+ collection . push ( environment ) ;
780+ }
781+ } catch ( error ) {
782+ traceError (
783+ `Failed to convert native env to Python environment: ${ e . prefix ?? e . executable } ` ,
784+ error ,
785+ ) ;
786+ log . error (
787+ `Failed to process conda environment ${ e . prefix ?? e . executable } : ${
788+ error instanceof Error ? error . message : String ( error )
789+ } `,
790+ ) ;
723791 }
724792 } ) ,
725793 ) ;
0 commit comments