microsoft/aspire · error · ProjectLocatorException

Multiple project files found.

Error message

Multiple project files found.

What it means

When multiple AppHost project files are found in one directory and the caller configured MultipleAppHostProjectsFoundBehavior.Throw, ProjectLocator refuses to guess and throws ProjectLocatorException with ErrorStrings.MultipleProjectFilesFound and reason MultipleProjectFilesFound. The behavior is a contract: callers who cannot prompt the user for a choice explicitly request an exception instead of ambiguous selection.

Solutions

  1. Pass --project <path> to explicitly select which AppHost to use, bypassing ambiguity.
  2. Remove or move the extra AppHost projects so only one remains in the directory.
  3. Use a MultipleAppHostProjectsFoundBehavior that fits the context (interactive selection instead of Throw) if prompting is acceptable.
  4. Catch ProjectLocatorException with FailureReason MultipleProjectFilesFound and enumerate the candidates for the user.

Example fix

// before
aspire run   # multiple AppHosts in ./src, Throw behavior
// after
aspire run --project ./src/AppHost1/AppHost1.csproj
Defensive patterns

Strategy: try-catch

Validate before calling

var appHosts = Directory.EnumerateFiles(dir, "*.csproj", SearchOption.AllDirectories)
    .Where(p => File.ReadAllText(p).Contains("Aspire.AppHost.Sdk"))
    .ToList();
if (appHosts.Count > 1) Console.WriteLine("Multiple AppHosts found: " + string.Join(", ", appHosts) + " — pass --project to disambiguate.");

Type guard

static bool IsAmbiguousAppHostDirectory(string dir) => Directory.EnumerateFiles(dir, "*.csproj", SearchOption.AllDirectories).Count(LooksLikeAppHost) > 1;

Try / catch

try { var appHost = await locator.UseAppHostAsync(ct); }
catch (ProjectLocatorException ex) when (ex.FailureReason == ProjectLocatorFailureReason.MultipleProjectFilesFound)
{ Console.WriteLine("Multiple AppHosts found — re-run with --project <csproj> to choose one."); }

Prevention

When it happens

Trigger: Multiple AppHost csproj files in the same scanned directory combined with MultipleAppHostProjectsFoundBehavior.Throw (typically in non-interactive/automation contexts where prompting is impossible).

Common situations: Merging several samples into one folder; copying a second AppHost project next to an existing one; scaffolding tools generating extra AppHost projects; CI scripts running in a directory with multiple AppHosts where interactive selection is unavailable.

Related errors


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

Appendix: source

Thrown at src/Aspire.Cli/Projects/ProjectLocator.cs:982

                    {
                        logger.LogDebug("Multiple AppHost project files found in directory {Directory}, prompting user to select", directory.FullName);
                        explicitSelectionWasPrompted = true;
                        projectFile = await interactionService.PromptForSelectionAsync(
                            InteractionServiceStrings.SelectAppHostToUse,
                            appHostProjects,
                            file => $"{file.Name.EscapeMarkup()} ({Path.GetRelativePath(executionContext.WorkingDirectory.FullName, file.FullName).EscapeMarkup()})",
                            cancellationToken: cancellationToken
                        );
                    }
                    else if (multipleAppHostProjectsFoundBehavior is MultipleAppHostProjectsFoundBehavior.None)
                    {
                        logger.LogDebug("Multiple AppHost project files found in directory {Directory}, selecting none", directory.FullName);
                        return new AppHostProjectSearchResult(null, appHostProjects);
                    }
                    else if (multipleAppHostProjectsFoundBehavior is MultipleAppHostProjectsFoundBehavior.Throw)
                    {
                        logger.LogError("Multiple AppHost project files found in directory {Directory}, throwing exception", directory.FullName);
                        throw new ProjectLocatorException(ErrorStrings.MultipleProjectFilesFound, ProjectLocatorFailureReason.MultipleProjectFilesFound);
                    }
                }
            }
            else if (File.Exists(projectFile.FullName))
            {
                // A project file was directly specified.
                //
                // Preserve symlinks because single-file AppHosts load apphost.run.json and
                // aspire.config.json beside the selected path. Backchannel and comparison call
                // sites canonicalize their own identity keys.
                var resolvedProjectPath = PathNormalizer.ResolvePathCasing(projectFile.FullName);

                if (!string.Equals(resolvedProjectPath, projectFile.FullName, StringComparison.Ordinal))
                {
                    logger.LogDebug(
                        "Normalized explicit AppHost path casing from '{OriginalPath}' to '{ResolvedPath}'.",
                        projectFile.FullName,
                        resolvedProjectPath);

View on GitHub (pinned to 25830f84bd)