microsoft/aspire · error · InvalidOperationException

Multiple NuGet.config files found in

Error message

Multiple NuGet.config files found in '{directory.FullName}' differing only by case.

What it means

NuGetConfigMerger scans a directory (top-level only) for files named nuget.config case-insensitively; if it finds more than one that differ only by case (e.g. NuGet.config and nuget.config), it throws because the correct file to merge into is ambiguous. The merge refuses to guess which config to modify.

Solutions

  1. Delete or rename one of the duplicate configs so only a single `nuget.config` remains.
  2. Merge the contents of both files into one canonical `NuGet.config`.
  3. Use `git ls-files | grep -i nuget.config` to find case-duplicated files tracked by git.
  4. On git, fix tracked casing with `git mv -f nuget.config NuGet.config` if the wrong casing is committed.

Example fix

// before (directory contains NuGet.config AND nuget.config)
// after: keep one canonical file
// git mv -f nuget.config NuGet.config
// git commit -m "Deduplicate NuGet.config casing"
Defensive patterns

Strategy: validation

Validate before calling

var dupes = Directory.EnumerateFiles(dir, "*", SearchOption.TopDirectoryOnly)
    .Where(f => string.Equals(Path.GetFileName(f), "nuget.config", StringComparison.OrdinalIgnoreCase))
    .ToList();
if (dupes.Count > 1) throw new InvalidOperationException($"Multiple NuGet.config casings found: {string.Join(", ", dupes)}");

Try / catch

try
{
    await merger.MergeAsync(targetDir, sources, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Multiple NuGet.config files"))
{
    Console.Error.WriteLine("Deduplicate NuGet.config casings before merging.");
}

Prevention

When it happens

Trigger: Running any NuGetConfigMerger operation (Create/merge flow) in a project or directory that contains both `NuGet.config` and `nuget.config` (or `NUGET.CONFIG`, etc.) at the top level.

Common situations: A repo created on a case-insensitive OS (Windows/macOS) later checked out on Linux showing both casings; manual edits adding a second config with different casing; tooling generated one and a developer hand-added another.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Cli/Packaging/NuGetConfigMerger.cs:935

        catch
        {
            // If we can't read the file, assume sources are missing
            return true;
        }
    }

    internal static bool TryFindNuGetConfigInDirectory(DirectoryInfo directory, [NotNullWhen(true)] out FileInfo? nugetConfigFile)
    {
        ArgumentNullException.ThrowIfNull(directory);
        // Find all files whose name matches "nuget.config" ignoring case in the top-level directory only
        var matches = directory
            .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
            .Where(f => string.Equals(f.Name, "nuget.config", StringComparison.OrdinalIgnoreCase))
            .ToArray();

        if (matches.Length > 1)
        {
            throw new InvalidOperationException($"Multiple NuGet.config files found in '{directory.FullName}' differing only by case.");
        }

        nugetConfigFile = matches.SingleOrDefault();
        return matches.Length == 1;
    }

    private static async Task AddGlobalPackagesFolderToConfigAsync(FileInfo configFile)
    {
        XDocument doc;
        await using (var stream = configFile.OpenRead())
        {
            doc = XDocument.Load(stream);
        }

        var configuration = doc.Root ?? throw new InvalidOperationException("Invalid NuGet config structure");
        AddGlobalPackagesFolderConfiguration(configuration);

        await using (var writeStream = configFile.Open(FileMode.Create, FileAccess.Write, FileShare.None))

View on GitHub (pinned to 25830f84bd)