@@ -326,6 +326,11 @@ class NativePythonFinderImpl implements NativePythonFinder {
326326 private connection : rpc . MessageConnection ;
327327 private readonly pool : WorkerPool < NativePythonEnvironmentKind | Uri [ ] | undefined , NativeInfo [ ] > ;
328328 private cache : Map < string , NativeInfo [ ] > = new Map ( ) ;
329+ /**
330+ * Tracks in-flight hard refreshes by cache key so concurrent callers share a
331+ * single PET scan instead of queueing duplicate work.
332+ */
333+ private inFlightRefreshes : Map < string , Promise < NativeInfo [ ] > > = new Map ( ) ;
329334 private startDisposables : Disposable [ ] = [ ] ;
330335 private proc : ChildProcess | undefined ;
331336 private processExited : boolean = false ;
@@ -555,20 +560,39 @@ class NativePythonFinderImpl implements NativePythonFinder {
555560
556561 private async handleHardRefresh ( options ?: NativePythonEnvironmentKind | Uri [ ] ) : Promise < NativeInfo [ ] > {
557562 const key = this . getKey ( options ) ;
563+
564+ const inFlight = this . inFlightRefreshes . get ( key ) ;
565+ if ( inFlight ) {
566+ this . outputChannel . debug ( `[Finder] Coalescing hard refresh with in-flight request for key: ${ key } ` ) ;
567+ return inFlight ;
568+ }
569+
558570 this . cache . delete ( key ) ;
559571 if ( ! options ) {
560572 this . outputChannel . debug ( '[Finder] Refreshing all environments' ) ;
561573 } else {
562574 this . outputChannel . debug ( `[Finder] Hard refresh for key: ${ key } ` ) ;
563575 }
564- const result = await this . pool . addToQueue ( options ) ;
565- // Validate result from worker pool
566- if ( ! result || ! Array . isArray ( result ) ) {
567- this . outputChannel . warn ( `[pet] Worker pool returned invalid result type: ${ typeof result } ` ) ;
568- return [ ] ;
569- }
570- this . cache . set ( key , result ) ;
571- return result ;
576+
577+ // .finally clears the in-flight slot on both success AND failure paths so
578+ // a rejected refresh does not poison the cache — the next call after a
579+ // failure starts a fresh attempt, matching today's behavior.
580+ const refreshPromise = this . pool
581+ . addToQueue ( options )
582+ . then ( ( result ) => {
583+ if ( ! result || ! Array . isArray ( result ) ) {
584+ this . outputChannel . warn ( `[pet] Worker pool returned invalid result type: ${ typeof result } ` ) ;
585+ return [ ] as NativeInfo [ ] ;
586+ }
587+ this . cache . set ( key , result ) ;
588+ return result ;
589+ } )
590+ . finally ( ( ) => {
591+ this . inFlightRefreshes . delete ( key ) ;
592+ } ) ;
593+
594+ this . inFlightRefreshes . set ( key , refreshPromise ) ;
595+ return refreshPromise ;
572596 }
573597
574598 private async handleSoftRefresh ( options ?: NativePythonEnvironmentKind | Uri [ ] ) : Promise < NativeInfo [ ] > {
0 commit comments