@@ -184,6 +184,7 @@ private static CondaInfo detectCondaInfo()
184184 return info ;
185185
186186 // Method 2: Parse shell config files (critical for macOS/Linux GUI apps)
187+ IJ .log ( "" );
187188 if ( isMac () || isLinux () )
188189 {
189190 info = detectFromShellConfig ();
@@ -194,19 +195,21 @@ private static CondaInfo detectCondaInfo()
194195 {
195196 IJ .log ( "Method 2: Parsing shell configuration files..." );
196197 IJ .log ( " Skipped (only applicable on macOS/Linux)" );
197- IJ .log ( "" );
198198 }
199199
200200 // Method 3: Search in PATH
201+ IJ .log ( "" );
201202 info = detectFromPath ();
202203 if ( info != null )
203204 return info ;
204205
205206 // Method 4: Check common installation locations (fallback)
207+ IJ .log ( "" );
206208 info = detectFromCommonLocations ();
207209 if ( info != null )
208210 return info ;
209211
212+ IJ .log ( "" );
210213 IJ .log ( "Failed to detect conda installation" );
211214 return null ;
212215 }
@@ -217,21 +220,32 @@ private static CondaInfo detectCondaInfo()
217220 private static CondaInfo detectFromEnvironment ()
218221 {
219222 IJ .log ( "Method 1: Checking CONDA_EXE environment variable..." );
223+
224+ // Check CONDA_EXE first
220225 String condaExe = System .getenv ( "CONDA_EXE" );
226+
227+ // Also check for _CONDA_EXE (sometimes set by conda)
228+ if ( condaExe == null || condaExe .isEmpty () )
229+ condaExe = System .getenv ( "_CONDA_EXE" );
230+
221231 if ( condaExe != null && new File ( condaExe ).exists () )
222232 {
223233 // Resolve symlinks/aliases
224234 condaExe = resolveRealExecutable ( condaExe );
225235
226- final String rootPrefix = deriveRootPrefix ( condaExe );
227- final String version = getCondaVersion ( condaExe );
228-
229- if ( rootPrefix != null && version != null )
236+ if ( condaExe != null )
230237 {
231- IJ .log ( " Found conda via CONDA_EXE: " + condaExe );
232- return new CondaInfo ( condaExe , rootPrefix , version );
238+ final String rootPrefix = deriveRootPrefix ( condaExe );
239+ final String version = getCondaVersion ( condaExe );
240+
241+ if ( rootPrefix != null && version != null )
242+ {
243+ IJ .log ( " Found conda via CONDA_EXE: " + condaExe );
244+ return new CondaInfo ( condaExe , rootPrefix , version );
245+ }
233246 }
234247 }
248+
235249 IJ .log ( " CONDA_EXE not set or invalid" );
236250 return null ;
237251 }
@@ -794,12 +808,25 @@ private static String findInPath( final String executable )
794808
795809 try
796810 {
797- final Process process = new ProcessBuilder ( command )
798- .redirectErrorStream ( true )
799- .start ();
811+ final ProcessBuilder pb = new ProcessBuilder ( command );
812+ pb .redirectErrorStream ( true );
813+ final Process process = pb .start ();
800814
801815 final String output = readProcessOutput ( process );
802- process .waitFor ( 5 , TimeUnit .SECONDS );
816+ final boolean completed = process .waitFor ( 5 , TimeUnit .SECONDS );
817+
818+ // Check exit code - 'where' returns non-zero when not found
819+ if ( !completed )
820+ {
821+ IJ .log ( " Command timed out searching for '" + executable + "'" );
822+ return null ;
823+ }
824+
825+ if ( process .exitValue () != 0 )
826+ {
827+ IJ .log ( " '" + executable + "' not found in PATH" );
828+ return null ;
829+ }
803830
804831 if ( output != null && !output .isEmpty () )
805832 {
@@ -814,6 +841,23 @@ private static String findInPath( final String executable )
814841 if ( path .isEmpty () )
815842 continue ;
816843
844+ // Skip error messages from Windows 'where' command
845+ if ( path .startsWith ( "INFO:" ) ||
846+ path .startsWith ( "ERROR:" ) ||
847+ path .startsWith ( "WARNING:" ) ||
848+ path .contains ( "Could not find" ) )
849+ {
850+ IJ .log ( " Skipping error message: " + path );
851+ continue ;
852+ }
853+
854+ // Validate that this looks like a real path
855+ if ( !isValidPath ( path ) )
856+ {
857+ IJ .log ( " Skipping invalid path format: " + path );
858+ continue ;
859+ }
860+
817861 // On Windows, skip conda in Library\bin (it's a helper
818862 // script)
819863 if ( isWindows () && path .contains ( "\\ Library\\ bin\\ conda" ) )
@@ -822,6 +866,13 @@ private static String findInPath( final String executable )
822866 continue ;
823867 }
824868
869+ // Verify the file actually exists
870+ if ( !new File ( path ).exists () )
871+ {
872+ IJ .log ( " Skipping non-existent path: " + path );
873+ continue ;
874+ }
875+
825876 // Resolve symlinks on Unix
826877 if ( !isWindows () )
827878 {
@@ -841,12 +892,61 @@ private static String findInPath( final String executable )
841892 }
842893 catch ( final Exception e )
843894 {
844- // Command failed
895+ IJ . log ( " Error searching PATH: " + e . getMessage () );
845896 }
846897
847898 return null ;
848899 }
849900
901+ /**
902+ * Validate that a string looks like a valid file path
903+ */
904+ private static boolean isValidPath ( final String path )
905+ {
906+ if ( path == null || path .isEmpty () )
907+ return false ;
908+
909+ // Check for obvious non-path patterns (error messages)
910+ if ( path .startsWith ( "INFO:" ) ||
911+ path .startsWith ( "ERROR:" ) ||
912+ path .startsWith ( "WARNING:" ) ||
913+ path .toLowerCase ().contains ( "could not find" ) ||
914+ path .toLowerCase ().contains ( "not recognized" ) )
915+ return false ;
916+
917+ if ( isWindows () )
918+ {
919+ // Windows paths should contain : (drive letter) or start with \\
920+ // (UNC)
921+ // or at least contain \ (subdirectory)
922+ if ( !path .contains ( ":" ) && !path .startsWith ( "\\ \\ " ) && !path .contains ( "\\ " ) )
923+ {
924+ // Might be a relative path - check if file exists
925+ if ( !new File ( path ).exists () )
926+ return false ;
927+ }
928+
929+ // Check for invalid Windows path characters that indicate error
930+ // messages
931+ final String invalidChars = "<>|\u0000 " ;
932+ for ( int i = 0 ; i < invalidChars .length (); i ++ )
933+ {
934+ if ( path .indexOf ( invalidChars .charAt ( i ) ) >= 0 )
935+ return false ;
936+ }
937+ }
938+ else
939+ {
940+ // Unix paths should start with / (absolute) or contain / (relative)
941+ // Simple heuristic: if it's long, has spaces, but no /, probably an
942+ // error message
943+ if ( !path .contains ( "/" ) && path .contains ( " " ) && path .length () > 30 )
944+ return false ;
945+ }
946+
947+ return true ;
948+ }
949+
850950 /**
851951 * Build path to conda/mamba/micromamba executable from installation
852952 * directory
0 commit comments