{"record":{"id":"7d6eb0a012d04262","repo":"microsoft/aspire","slug":"browsermessagestrings-browserhostregistry","errorCode":null,"errorMessage":"BrowserMessageStrings.BrowserLogsTrackedBrowserProfileConflict","messagePattern":"BrowserMessageStrings\\.BrowserLogsTrackedBrowserProfileConflict","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Browsers/BrowserHostRegistry.cs","lineNumber":365,"sourceCode":"        return profile;\n    }\n\n    private static void ValidateProfileCompatibility(BrowserHostIdentity identity, string? existingProfileDirectoryName, string? requestedProfileDirectoryName)\n    {\n        // A request without an explicit profile can attach to any tracked browser for the same user data root. Once a\n        // caller asks for a named profile, however, reusing a host launched for a different profile would put the session\n        // in the wrong browser context, so fail instead of silently attaching to the wrong profile.\n        // Profile directory names are case-insensitive on Windows and macOS (default APFS) but case-sensitive on Linux.\n        // We compare with OrdinalIgnoreCase intentionally so a request for \"default\" attaches to a host that was\n        // launched with \"Default\": Chromium itself accepts either casing on Windows/macOS, and on Linux the user is\n        // expected to specify the literal directory name. We err on the side of attaching rather than rejecting.\n        if (requestedProfileDirectoryName is null ||\n            string.Equals(existingProfileDirectoryName, requestedProfileDirectoryName, StringComparison.OrdinalIgnoreCase))\n        {\n            return;\n        }\n\n        throw new InvalidOperationException(\n            string.Format(\n                CultureInfo.CurrentCulture,\n                BrowserMessageStrings.BrowserLogsTrackedBrowserProfileConflict,\n                identity.UserDataRootPath,\n                existingProfileDirectoryName ?? BrowserMessageStrings.BrowserLogsDefaultProfileName,\n                requestedProfileDirectoryName));\n    }\n\n    private static string FormatDebugEndpoint(Uri? debugEndpoint) =>\n        debugEndpoint?.ToString() ?? \"private CDP pipe\";\n\n    private sealed class BrowserHostEntry(IBrowserHost host, string? profileDirectoryName, int ReferenceCount)\n    {\n        public IBrowserHost Host { get; } = host;\n\n        public string? ProfileDirectoryName { get; } = profileDirectoryName;\n\n        public int ReferenceCount { get; set; } = ReferenceCount;","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Browsers/BrowserHostRegistry.cs#L347-L383","documentation":"ValidateProfileCompatibility (called from AcquireAsync) throws when a browser host for the same executable + user data root already exists under a tracked profile directory whose name differs from the requested one. One user data root maps to exactly one tracked profile; honoring a conflicting request would corrupt session isolation, so the registry refuses it.","triggerScenarios":"AcquireAsync with a configuration whose resolved user data root already hosts a tracked browser with a different profile directory name — e.g. the first lease used the default profile and a later lease requests a custom profile name for the same root.","commonSituations":"Mixing lease acquisitions with different profile settings for the same browser; two app hosts or two components sharing one user data directory but configured with different profile names; config drift between runs.","solutions":["Use one consistent profile configuration per (browser executable, user data root) pair.","Request a different user data root if you need a different profile directory.","Release all existing leases and let the tracked host shut down before re-acquiring with a different profile."],"exampleFix":"// before\nvar first = await registry.AcquireAsync(new BrowserConfiguration { Browser = b, ProfileName = \"default\" }, ct);\nvar second = await registry.AcquireAsync(new BrowserConfiguration { Browser = b, ProfileName = \"automation\" }, ct); // same root, conflict\n\n// after\nvar second = await registry.AcquireAsync(new BrowserConfiguration { Browser = b, ProfileName = \"automation\", UserDataRoot = alternateRoot }, ct);","handlingStrategy":"validation","validationCode":"// Keep a single profile name per user data root across the application\nprivate const string TrackedProfileName = \"aspire-tracked\"; // reuse everywhere instead of ad-hoc names","typeGuard":null,"tryCatchPattern":"try { lease = await registry.AcquireAsync(config, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"profile\", StringComparison.OrdinalIgnoreCase)) { logger.LogError(ex, \"Profile conflict on {Root}; use a distinct user data root or the existing profile.\", config.UserDataRoot); }","preventionTips":["Centralize browser profile configuration in one constant/options type.","Never share a user data directory between differently configured hosts.","Release all leases before changing profile settings."],"tags":["browser","profile-conflict","user-data-directory","state-conflict"],"backgroundTag":"conflicting-config-options","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"}