{"record":{"id":"1554468c07196086","repo":"OrchardCMS/OrchardCore","slug":"the-orchardcore-signalr-azure-and-orchardcore-signalr-redis-155446","errorCode":null,"errorMessage":"The 'OrchardCore.SignalR.Azure' and 'OrchardCore.SignalR.Redis' features cannot be enabled together.","messagePattern":"The 'OrchardCore\\.SignalR\\.Azure' and 'OrchardCore\\.SignalR\\.Redis' features cannot be enabled together\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore.Modules/OrchardCore.SignalR.Redis/Startup.cs","lineNumber":43,"sourceCode":"            return;\n        }\n\n        EnsureNoBackplaneIsRegistered(services);\n\n        services\n            .AddSignalR()\n            .AddStackExchangeRedis();\n\n        services.AddTransient<IConfigureOptions<SignalRRedisOptions>, SignalRRedisOptionsConfiguration>();\n    }\n\n    private static void EnsureNoBackplaneIsRegistered(IServiceCollection services)\n    {\n        if (services.Any(descriptor =>\n            descriptor.IsKeyedService &&\n            string.Equals(descriptor.ServiceKey as string, BackplaneRegistrationKey, StringComparison.Ordinal)))\n        {\n            throw new InvalidOperationException(\n                $\"The '{AzureBackplaneFeature}' and '{RedisBackplaneFeature}' features cannot be enabled together.\");\n        }\n\n        services.AddKeyedSingleton<object>(BackplaneRegistrationKey, new object());\n    }\n}\n\ninternal sealed class SignalRRedisOptionsConfiguration : IConfigureOptions<SignalRRedisOptions>\n{\n    private readonly RedisOptions _redisOptions;\n    private readonly string _tenantName;\n\n    public SignalRRedisOptionsConfiguration(IOptions<RedisOptions> redisOptions, ShellSettings shellSettings)\n    {\n        _redisOptions = redisOptions.Value;\n        _tenantName = shellSettings.Name;\n    }\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore.Modules/OrchardCore.SignalR.Redis/Startup.cs#L25-L61","documentation":"Mirror implementation of the backplane guard in the SignalR.Redis module: when its Startup runs, it checks the service collection for the backplane registration marker and throws InvalidOperationException if the Azure backplane feature was already enabled, preventing two SignalR backplanes on one tenant.","triggerScenarios":"Enabling both OrchardCore.SignalR.Redis and OrchardCore.SignalR.Azure features for the same tenant; a recipe enabling both; enabling Redis after Azure in the same request pipeline setup.","commonSituations":"Admin enables Azure SignalR while Redis is already configured via appsettings-driven feature list; duplicated recipes; test tenants that combined both backplane features.","solutions":["Disable either the Redis or the Azure SignalR feature so exactly one backplane is active.","Remove one of the features from recipe/setup JSON that provisions the tenant.","Choose the desired backplane up front and document it to prevent toggling both on."],"exampleFix":"// before (tenant features)\n\"OrchardCore.SignalR.Azure\", \"OrchardCore.SignalR.Redis\"\n// after\n\"OrchardCore.SignalR.Azure\"","handlingStrategy":"validation","validationCode":"var backplanes = new[] { \"OrchardCore.SignalR.Redis\", \"OrchardCore.SignalR.Azure\" }\n    .Where(f => tenantFeatures.Contains(f)).ToList();\nif (backplanes.Count > 1) throw new InvalidOperationException($\"Only one backplane allowed, found: {string.Join(',', backplanes)}\");","typeGuard":"const hasSingleBackplane = (fs) => ['OrchardCore.SignalR.Redis','OrchardCore.SignalR.Azure'].filter(f => fs.includes(f)).length === 1;","tryCatchPattern":"try {\n  await enableFeatureAsync(\"OrchardCore.SignalR.Redis\");\n} catch (InvalidOperationException ex) when (ex.Message.Contains(\"cannot be enabled together\")) {\n  logger.LogWarning(\"Azure backplane already enabled; pick one.\");\n}","preventionTips":["Never combine Azure and Redis SignalR features in a tenant or recipe.","Keep a canonical environment config listing the intended backplane.","Test tenant provisioning recipes before production rollout."],"tags":["signalr","backplane","redis","azure","conflicting-features"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}