{"record":{"id":"3dc4787f6acb4fa4","repo":"microsoft/aspire","slug":"multiple-project-files-found","errorCode":null,"errorMessage":"Multiple project files found.","messagePattern":"Multiple project files found\\.","errorType":"exception","errorClass":"ProjectLocatorException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Projects/ProjectLocator.cs","lineNumber":982,"sourceCode":"                    {\n                        logger.LogDebug(\"Multiple AppHost project files found in directory {Directory}, prompting user to select\", directory.FullName);\n                        explicitSelectionWasPrompted = true;\n                        projectFile = await interactionService.PromptForSelectionAsync(\n                            InteractionServiceStrings.SelectAppHostToUse,\n                            appHostProjects,\n                            file => $\"{file.Name.EscapeMarkup()} ({Path.GetRelativePath(executionContext.WorkingDirectory.FullName, file.FullName).EscapeMarkup()})\",\n                            cancellationToken: cancellationToken\n                        );\n                    }\n                    else if (multipleAppHostProjectsFoundBehavior is MultipleAppHostProjectsFoundBehavior.None)\n                    {\n                        logger.LogDebug(\"Multiple AppHost project files found in directory {Directory}, selecting none\", directory.FullName);\n                        return new AppHostProjectSearchResult(null, appHostProjects);\n                    }\n                    else if (multipleAppHostProjectsFoundBehavior is MultipleAppHostProjectsFoundBehavior.Throw)\n                    {\n                        logger.LogError(\"Multiple AppHost project files found in directory {Directory}, throwing exception\", directory.FullName);\n                        throw new ProjectLocatorException(ErrorStrings.MultipleProjectFilesFound, ProjectLocatorFailureReason.MultipleProjectFilesFound);\n                    }\n                }\n            }\n            else if (File.Exists(projectFile.FullName))\n            {\n                // A project file was directly specified.\n                //\n                // Preserve symlinks because single-file AppHosts load apphost.run.json and\n                // aspire.config.json beside the selected path. Backchannel and comparison call\n                // sites canonicalize their own identity keys.\n                var resolvedProjectPath = PathNormalizer.ResolvePathCasing(projectFile.FullName);\n\n                if (!string.Equals(resolvedProjectPath, projectFile.FullName, StringComparison.Ordinal))\n                {\n                    logger.LogDebug(\n                        \"Normalized explicit AppHost path casing from '{OriginalPath}' to '{ResolvedPath}'.\",\n                        projectFile.FullName,\n                        resolvedProjectPath);","sourceCodeStart":964,"sourceCodeEnd":1000,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Projects/ProjectLocator.cs#L964-L1000","documentation":"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.","triggerScenarios":"Multiple AppHost csproj files in the same scanned directory combined with MultipleAppHostProjectsFoundBehavior.Throw (typically in non-interactive/automation contexts where prompting is impossible).","commonSituations":"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.","solutions":["Pass --project <path> to explicitly select which AppHost to use, bypassing ambiguity.","Remove or move the extra AppHost projects so only one remains in the directory.","Use a MultipleAppHostProjectsFoundBehavior that fits the context (interactive selection instead of Throw) if prompting is acceptable.","Catch ProjectLocatorException with FailureReason MultipleProjectFilesFound and enumerate the candidates for the user."],"exampleFix":"// before\naspire run   # multiple AppHosts in ./src, Throw behavior\n// after\naspire run --project ./src/AppHost1/AppHost1.csproj","handlingStrategy":"try-catch","validationCode":"var appHosts = Directory.EnumerateFiles(dir, \"*.csproj\", SearchOption.AllDirectories)\n    .Where(p => File.ReadAllText(p).Contains(\"Aspire.AppHost.Sdk\"))\n    .ToList();\nif (appHosts.Count > 1) Console.WriteLine(\"Multiple AppHosts found: \" + string.Join(\", \", appHosts) + \" — pass --project to disambiguate.\");","typeGuard":"static bool IsAmbiguousAppHostDirectory(string dir) => Directory.EnumerateFiles(dir, \"*.csproj\", SearchOption.AllDirectories).Count(LooksLikeAppHost) > 1;","tryCatchPattern":"try { var appHost = await locator.UseAppHostAsync(ct); }\ncatch (ProjectLocatorException ex) when (ex.FailureReason == ProjectLocatorFailureReason.MultipleProjectFilesFound)\n{ Console.WriteLine(\"Multiple AppHosts found — re-run with --project <csproj> to choose one.\"); }","preventionTips":["Keep one AppHost project per scanned directory; move extras elsewhere.","Always pass --project in non-interactive/CI contexts.","Choose MultipleAppHostProjectsFoundBehavior matching the environment (Throw for automation, selection for interactive).","Handle the MultipleProjectFilesFound failure reason explicitly in scripts to list candidates."],"tags":["cli","project-resolution","apphost","ambiguity"],"backgroundTag":"ambiguous-project-selection","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}