microsoft/aspire · error · ProjectUpdaterException

The project file was updated successfully, but the…

Error message

The project file was updated successfully, but the PackageVersion entry for '{packageId}' could not be removed from '{cpmInfo.DirectoryPackagesPropsFile.FullName}': {ex.Message}. Please manually remove the <PackageVersion Include="{packageId}" ... /> entry from this file to avoid NU1009 build errors.

What it means

During CPM (Central Package Management) migration, the updater first updates the csproj to use Aspire.* package versions, then removes the corresponding <PackageVersion> entries from Directory.Packages.props. If that removal throws (file locked, save failure, parse error), the csproj is already modified and cannot be rolled back, so this exception tells the user the exact manual step left to avoid NU1009 (transitive Central Package Management) errors.

Solutions

  1. Manually remove the <PackageVersion Include="{packageId}" ... /> entry for the named package from Directory.Packages.props, then build to confirm no NU1009 error.
  2. Close editors/lockers or fix file permissions on Directory.Packages.props and re-run `aspire update`.
  3. If the file is malformed XML, repair or restore it before hand-editing the PackageVersion entry.

Example fix

// before (Directory.Packages.props)
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" />
// after
<!-- entry removed -->
Defensive patterns

Strategy: try-catch

Validate before calling

var propsPath = Path.Combine(repoRoot, "Directory.Packages.props");
if (File.Exists(propsPath))
{
    using var stream = File.Open(propsPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // throws if locked/read-only
    var doc = new XmlDocument(); doc.Load(stream); // throws if malformed
}

Try / catch

try { await updater.UpdateAsync(...); }
catch (ProjectUpdaterException ex) when (ex.Message.Contains("could not be removed"))
{
    // follow the message: remove the named <PackageVersion> entry from Directory.Packages.props manually
}

Prevention

When it happens

Trigger: ProjectUpdater removing a PackageVersion entry from Directory.Packages.props after already saving the updated csproj, and the props-file write/parse fails — e.g. Directory.Packages.props is read-only, locked by another process, or malformed XML.

Common situations: Directory.Packages.props open in an editor or held by a build; insufficient write permissions on the props file; Directory.Packages.props with XML that fails to parse mid-migration.

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


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/34f399cb5ef3df33. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Projects/ProjectUpdater.cs:859

                return;
            }

            var parentNode = packageVersionNode.ParentNode;

            RemoveNodeWithWhitespace(packageVersionNode);

            if (parentNode.Name == "ItemGroup" && IsEmptyOrWhitespace(parentNode))
            {
                RemoveNodeWithWhitespace(parentNode);
            }

            propsDocument.Save(cpmInfo.DirectoryPackagesPropsFile.FullName);
        }
        catch (Exception ex)
        {
            // The csproj has already been updated at this point, so we can't roll back.
            // Inform the user what manual step is needed to complete the migration.
            throw new ProjectUpdaterException(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "The project file was updated successfully, but the PackageVersion entry for '{0}' could not be " +
                    "removed from '{1}': {2}. Please manually remove the <PackageVersion Include=\"{0}\" ... /> " +
                    "entry from this file to avoid NU1009 build errors.",
                    packageId,
                    cpmInfo.DirectoryPackagesPropsFile.FullName,
                    ex.Message),
                ex);
        }
    }

    private static async Task UpdateSdkVersionInSingleFileAppHostAsync(FileInfo projectFile, NuGetPackageCli package)
    {
        var fileContent = await File.ReadAllTextAsync(projectFile.FullName);

        // Look for the #:sdk Aspire.AppHost.Sdk@<version> directive
        var match = SdkDirectiveRegex().Match(fileContent);

View on GitHub (pinned to 25830f84bd)