Skip to content

fix: preserve conda environments on transient discovery failure - #12

Closed
StellaHuang95 wants to merge 1 commit into
mainfrom
preserve-conda-results-on-error
Closed

fix: preserve conda environments on transient discovery failure#12
StellaHuang95 wants to merge 1 commit into
mainfrom
preserve-conda-results-on-error

Conversation

@StellaHuang95

@StellaHuang95 StellaHuang95 commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Context

Conda environment discovery runs the native finder (PET). When that refresh throws/rejects — a
transient failure — refreshCondaEnvs previously returned [], the same value it returns for a
genuine "no conda environments" result. CondaEnvManager could not tell the two apart, so a
transient failure replaced the known-good collection with [] and emitted spurious remove
events, making previously-discovered environments disappear from the UI until the next successful
refresh.

Root cause

refreshCondaEnvs collapsed two distinct outcomes into []:

  • the native finder threw (discovery failed), and
  • the native finder succeeded with zero environments.

Every manager discovery path (initialize, refresh, background get) then assigned
this.collection = result and emitted add/remove events, so a failure wiped the collection.

Fix

  • refreshCondaEnvs now returns PythonEnvironment[] | undefined:
    • undefined = discovery failed (native finder threw/rejected).
    • an array (including []) = authoritative success; [] means genuinely no environments.
  • The three manager discovery paths guard on undefined:
    • Failure: keep the existing collection and emit no removals, but still call loadEnvMap()
      so persisted global/workspace selections are restored — announcing only environments newly
      appended by that resolution (loadEnvMapPreservingCollection).
    • Success (including []): replace the collection and emit the normal add/remove events.

Legitimate deletion is unchanged: a successful [] is authoritative and removes stale environments
exactly as before. Only a thrown-failure undefined is treated as "preserve".

Tests

  • condaUtils.refreshCondaEnvs.unit.test.ts: the utility returns undefined on a rejected refresh
    and [] on a successful empty discovery.
  • condaEnvManager.resultPreservation.unit.test.ts:
    • a failed refresh/initialize preserves the known-good collection and emits no changes when
      nothing persisted resolves;
    • a successful [] empties the collection and emits removals; a successful non-empty result
      replaces the collection and emits removals + adds;
    • a failed discovery still restores a persisted global or workspace selection, emitting only its
      addition and never a removal, and retains it across get calls.

@StellaHuang95 StellaHuang95 changed the title fix: preserve conda environments when discovery fails fix: preserve conda environments and persisted selections when discovery fails Aug 22, 2026
@StellaHuang95

Copy link
Copy Markdown
Owner Author

🔒 Automated review in progress — @StellaHuang95 is auto-reviewing this PR.

...new Set(
events.filter((e) => e.kind === EnvironmentChangeKind.add).map((e) => e.environment.name),
),
];

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 src/test/managers/conda/condaEnvManager.resultPreservation.unit.test.ts:255
The Set deduplicates addition names, so this test passes even if the background path emits duplicate add events. Assert the raw addition count and ordered names instead.

[verified]

Comment thread src/managers/conda/condaEnvManager.ts Outdated
traceVerbose(
'Conda discovery failed during initialization; preserving collection and restoring persisted selections.',
);
await this.preserveCollectionOnFailedDiscovery();

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 src/managers/conda/condaEnvManager.ts:131
The same failure-result interpretation is repeated at all three discovery entry points, leaving the preservation invariant vulnerable to drift. Centralize applying a discovery result in a private helper, or otherwise keep the branches mechanically linked.

[verified]

@StellaHuang95 StellaHuang95 added review-auto:approved Automated review: no blocking findings (approval posted). bug Something isn't working labels Aug 22, 2026
@StellaHuang95 StellaHuang95 changed the title fix: preserve conda environments and persisted selections when discovery fails fix: preserve conda environments on transient discovery failure (and fix fast-path/concurrency races) Aug 22, 2026
Comment thread src/managers/conda/condaEnvManager.ts Outdated
// concurrent background discovery failure (whose own re-resolution may also fail)
// cannot lose it. Registration is silent: the discovery paths announce the collection,
// and failure recovery reuses this entry via normalized-path identity.
this.registerFastPathEnv(scope, fastResult.env);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

📍 src/managers/conda/condaEnvManager.ts:394
The Skeptic reproduced a race where successful background discovery finishes before the fast path, after which this silently inserts an environment into the already-announced authoritative collection. Reconcile this completion order by emitting an add when necessary or routing the late result through a common post-discovery application path, and add a gated regression test.

[verified]

// and never emit removals, but still restore persisted global/workspace
// selections (they resolve independently of discovery). A successful (possibly
// empty) array is applied normally.
if (refreshed === undefined) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 src/managers/conda/condaEnvManager.ts:131
The prior maintainability concern is only partially addressed: recovery is shared, but three callers still independently interpret undefined. Centralize discovery-result interpretation so future call sites cannot accidentally collapse failure into an authoritative empty result while retaining their distinct success-event policies.

[verified]

@StellaHuang95 StellaHuang95 added review-auto:changes-requested Automated review: posted blocking findings to address. and removed review-auto:approved Automated review: no blocking findings (approval posted). labels Aug 22, 2026
@StellaHuang95 StellaHuang95 changed the title fix: preserve conda environments on transient discovery failure (and fix fast-path/concurrency races) fix: preserve conda environments on transient discovery failure (failure vs authoritative-empty) Aug 23, 2026
@StellaHuang95 StellaHuang95 added review-auto:approved Automated review: no blocking findings (approval posted). and removed review-auto:changes-requested Automated review: posted blocking findings to address. labels Aug 23, 2026
@StellaHuang95
StellaHuang95 force-pushed the preserve-conda-results-on-error branch from 1f91115 to 03b74b0 Compare August 23, 2026 01:54
@StellaHuang95 StellaHuang95 changed the title fix: preserve conda environments on transient discovery failure (failure vs authoritative-empty) fix: preserve conda environments on transient discovery failure Aug 23, 2026
@StellaHuang95 StellaHuang95 added review-auto:approved Automated review: no blocking findings (approval posted). and removed review-auto:approved Automated review: no blocking findings (approval posted). labels Aug 23, 2026
@StellaHuang95
StellaHuang95 force-pushed the preserve-conda-results-on-error branch from 03b74b0 to 1484ce6 Compare August 23, 2026 02:08
@StellaHuang95 StellaHuang95 removed the review-auto:approved Automated review: no blocking findings (approval posted). label Aug 23, 2026
refreshCondaEnvs now returns undefined when the native finder throws/rejects, distinct from an authoritative successful empty array. CondaEnvManager guards its initialize, refresh, and background-get discovery paths so a transient failure preserves the known-good collection and emits no removals, while a successful empty result still removes stale environments normally. Persisted global/workspace selections are still restored via loadEnvMap on the failure path, announcing only newly appended environments.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@StellaHuang95
StellaHuang95 force-pushed the preserve-conda-results-on-error branch from 1484ce6 to 8d4f02e Compare August 23, 2026 02:12
this.collection = refreshed;
await this.loadEnvMap();
this._onDidChangeEnvironments.fire(
this.collection.map((e) => ({

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

📍 src/managers/conda/condaEnvManager.ts:362
[verified] The prior successful-background/late-fast-path race remains: this assignment can publish the authoritative background collection before a later fast-path result is reconciled, leaving that result without an add event. Route the late result through a common reconciliation path and add a gated successful-background regression test.

[verified]

this.collection =
(await refreshCondaEnvs(false, this.nativeFinder, this.api, this.log, this)) ?? [];
const refreshed = await refreshCondaEnvs(false, this.nativeFinder, this.api, this.log, this);
if (refreshed === undefined) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 src/managers/conda/condaEnvManager.ts:127
The two prior centralization warnings are only partially addressed: initialization, refresh, and background initialization still interpret undefined independently. Centralize discovery-result interpretation while parameterizing each caller's distinct successful-result event policy.

[verified]

@StellaHuang95 StellaHuang95 added the review-auto:changes-requested Automated review: posted blocking findings to address. label Aug 23, 2026
await this.loadEnvMapPreservingCollection();
return;
}
this.collection = refreshed;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

The initialization, refresh, and background initialization paths still independently classify undefined. Centralize discovery-result classification while preserving each caller's distinct success-event policy. [verified]

await this.loadEnvMap();
const added = this.collection.filter((env) => !known.has(env));
if (added.length > 0) {
this._onDidChangeEnvironments.fire(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

The pre-await known snapshot can become stale if another resolution appends and announces an environment while loadEnvMap() is awaiting, causing this helper to emit a duplicate add. Have loadEnvMap() report appended environments, or recheck announcement state before firing. [verified]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-auto:changes-requested Automated review: posted blocking findings to address.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant