microsoft/aspire · error · InvalidOperationException
Could not replace Aspire skills cache directory
Error message
Could not replace Aspire skills cache directory '{0}'. What it means
AspireSkillsInstaller attempts an atomic replacement of the Aspire skills cache directory by deleting the existing target and moving a staged directory into place. This error is thrown when the old target directory still exists after a delete was attempted, because the MoveTo would otherwise fail or merge incorrectly. It almost always indicates an external process (or antivirus/indexer) is holding a lock on files inside the directory, or the user lacks delete permission.
Solutions
- Close other aspire/CLI processes and editors that may hold handles under the skills cache directory, then retry the command.
- Manually delete the cache directory reported in the message (checking for read-only files) and re-run the install.
- Temporarily exclude the cache directory from antivirus/indexing if locks recur.
- Re-run the command in an elevated shell if the cache is under a protected path (e.g. Program Files).
- Retry after a short delay — transient Windows locks (e.g. from AV scans) often clear on their own.
Example fix
// before shell: rm -rf partially-locked-cache && aspire skills install // after # close other CLI sessions first, then rm -rf ~/.aspire/skills/cache/<version> aspire skills install
Defensive patterns
Strategy: retry
Try / catch
try
{
await installer.InstallAsync(...);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Could not replace Aspire skills cache directory"))
{
// wait for locks to clear, close other processes, then retry once
} Prevention
- Avoid running multiple aspire CLI commands that touch the skills cache concurrently.
- Keep the skills cache outside antivirus/indexer-heavy and file-sync (OneDrive/Dropbox) directories.
- Ensure the cache path is user-writable and not under a protected system directory.
When it happens
Trigger: Occurs during skill cache install/refresh when Directory.Exists(targetDir) is still true after TryDeleteDirectory(targetDir). Caused by open file handles in the cache dir, read-only attributes, antivirus scans, or insufficient ACLs on the cache path.
Common situations: Another aspire CLI process or editor is concurrently reading the skills cache; Windows Defender or a search indexer temporarily locks files; the cache lives on a network share or synced folder (OneDrive/Dropbox) with lock/pin semantics; running under a restricted service account.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Aspire skills bundle version
- Failed to acquire file lock
- The Aspire skills bundle staging directory must have a…
- Unable to determine current CLI location.
- Already connected to AppHost backchannel.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/46bea6996fe634af.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Agents/AspireSkills/AspireSkillsInstaller.cs:811
version,
archiveSha512,
githubArchiveSha256,
requireVerifiedGitHubSource: source == BundleArchiveSource.VerifiedGitHub,
skipCompatibilityCheck: source == BundleArchiveSource.Embedded,
activity: null,
cancellationToken).ConfigureAwait(false);
if (cachedResult is not null)
{
return cachedResult.Bundle!;
}
if (Directory.Exists(targetDir))
{
logger.LogDebug("Replacing Aspire skills cache directory {CacheDirectory}.", targetDir);
TryDeleteDirectory(targetDir);
if (Directory.Exists(targetDir))
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not replace Aspire skills cache directory '{0}'.", targetDir));
}
}
Directory.CreateDirectory(versionCacheDirectory);
RemoveLegacyCacheLayout(versionCacheDirectory);
stageDirectory.MoveTo(targetDir);
TouchLastUsed(targetDir);
return stagedBundle;
}
private static void RemoveInstallerMetadata(string bundleDirectory)
{
// These files describe local installer state, not bundle content. Always recreate them
// from the acquisition path so an archive cannot claim freshness or GitHub provenance.
File.Delete(Path.Combine(bundleDirectory, ArchiveSha512FileName));
File.Delete(Path.Combine(bundleDirectory, GitHubArchiveSha256FileName));
File.Delete(Path.Combine(bundleDirectory, GitHubAttestationVerifiedFileName));View on GitHub (pinned to 25830f84bd)