{"record":{"id":"1a996225fc9d02b9","repo":"microsoft/aspire","slug":"browsermessagestrings-browserlogsunabletolocatebrowser","errorCode":null,"errorMessage":"BrowserMessageStrings.BrowserLogsUnableToLocateBrowser","messagePattern":"BrowserMessageStrings\\.BrowserLogsUnableToLocateBrowser","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Browsers/BrowserHostRegistry.cs","lineNumber":57,"sourceCode":"        TimeProvider timeProvider,\n        Func<BrowserConfiguration, string, BrowserLogsUserDataDirectory>? createUserDataDirectory,\n        Func<BrowserConfiguration, BrowserHostIdentity, BrowserLogsUserDataDirectory, CancellationToken, Task<IBrowserHost>>? createHostAsync,\n        bool enableEndpointMetadataAdoption = false)\n    {\n        _endpointDiscovery = new BrowserEndpointDiscovery(logger);\n        _createUserDataDirectory = createUserDataDirectory ?? CreateUserDataDirectory;\n        _createHostAsync = createHostAsync ?? CreateHostCoreAsync;\n        _enableEndpointMetadataAdoption = enableEndpointMetadataAdoption;\n        _logger = logger;\n        _timeProvider = timeProvider;\n    }\n\n    public async Task<BrowserHostLease> AcquireAsync(BrowserConfiguration configuration, CancellationToken cancellationToken)\n    {\n        ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this);\n\n        var browserExecutable = ChromiumBrowserResolver.TryResolveExecutable(configuration.Browser)\n            ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, BrowserMessageStrings.BrowserLogsUnableToLocateBrowser, configuration.Browser));\n        var userDataDirectory = _createUserDataDirectory(configuration, browserExecutable);\n        var identity = new BrowserHostIdentity(browserExecutable, userDataDirectory.Path);\n\n        // The core AcquireAsync flow has to make one atomic decision per browser identity:\n        //\n        // 1. If the registry already has a host for this executable + user data root, reuse it and increment the lease\n        //    count.\n        // 2. Otherwise, create a host exactly once and publish it into the registry with the first lease.\n        //\n        // Keep the lock held across CreateHostCoreAsync. That method starts a new process by default, and can adopt a\n        // WebSocket endpoint when an explicit attach mode enables endpoint metadata. If two callers ran that decision\n        // concurrently they could both miss the dictionary entry and race to adopt/start a browser for the same profile.\n        var lockAcquired = false;\n        var hostPublished = false;\n        try\n        {\n            lockAcquired = await TryWaitForLockAsync(cancellationToken).ConfigureAwait(false);\n            ObjectDisposedException.ThrowIf(!lockAcquired, this);","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Browsers/BrowserHostRegistry.cs#L39-L75","documentation":"BrowserHostRegistry.AcquireAsync throws this before launching anything when ChromiumBrowserResolver.TryResolveExecutable cannot find a Chromium-family browser executable matching configuration.Browser. A tracked browser host needs a real browser binary; without one the lease cannot be created. It surfaces as an InvalidOperationException built from a localized resource string.","triggerScenarios":"Calling AcquireAsync (directly or via lease acquisition) with a BrowserConfiguration whose Browser value resolves to no installed executable: browser not installed, installed at a non-standard path, or running in a container/CI image without any Chromium browser.","commonSituations":"Fresh dev machine with no Chrome/Edge; slim Linux containers or GitHub Actions runners lacking browsers; user selects a specific browser type that is not installed; custom browser install location not on PATH or standard registry locations.","solutions":["Install a supported Chromium-based browser (Chrome, Edge, or Chromium) on the machine.","Verify the browser is discoverable: on PATH or in standard install locations for the OS.","Request a Browser value that matches a browser actually installed (e.g. fall back from Chrome to Edge).","Add browser installation steps to CI container images or setup scripts."],"exampleFix":"// before\nvar config = new BrowserConfiguration { Browser = SupportedBrowser.Chrome }; // Chrome not installed\nvar lease = await registry.AcquireAsync(config, ct);\n\n// after\nvar browser = ChromiumBrowserResolver.CanResolve(SupportedBrowser.Chrome)\n    ? SupportedBrowser.Chrome\n    : SupportedBrowser.Edge; // install or select an available browser","handlingStrategy":"validation","validationCode":"// Verify a Chromium browser is available before acquiring\nstatic bool HasChromiumBrowser() =>\n    OperatingSystem.IsWindows() && (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), \"Google/Chrome/Application/chrome.exe\")) || File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), \"Microsoft/Edge/Application/msedge.exe\")))\n    || OperatingSystem.IsLinux() && (File.Exists(\"/usr/bin/google-chrome\") || File.Exists(\"/usr/bin/chromium\") || File.Exists(\"/usr/bin/microsoft-edge\"))\n    || OperatingSystem.IsMacOS() && Directory.Exists(\"/Applications/Google Chrome.app\");","typeGuard":null,"tryCatchPattern":"try { lease = await registry.AcquireAsync(config, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"browser\", StringComparison.OrdinalIgnoreCase)) { logger.LogWarning(ex, \"No supported Chromium browser found; skipping browser logs.\"); }","preventionTips":["Install Chrome/Edge/Chromium in dev images and CI containers.","Pin the browser type your app depends on in setup docs.","Probe browser availability once at startup and log a clear warning instead of failing later."],"tags":["browser","chromium","executable-resolution","environment"],"backgroundTag":"file-not-found","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"}