diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs
index 88b1e44fb..7abc3f042 100644
--- a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs
+++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs
@@ -63,6 +63,7 @@ public interface ITenantService
///
/// Invokes the and caches the result.
/// When new tenants are added, lifecycle events are triggered to ensure background tasks are updated.
+ /// When the provider returns an empty list, is activated so that startup tasks run.
///
Task RefreshAsync(CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs
index 6a1f9cb98..5dc7f0c05 100644
--- a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs
+++ b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs
@@ -77,7 +77,10 @@ public class DefaultTenantService(IServiceScopeFactory scopeFactory, ITenantScop
var tenantsProvider = scope.ServiceProvider.GetRequiredService();
var currentTenants = await GetTenantsDictionaryAsync(cancellationToken);
var currentTenantIds = currentTenants.Keys;
- var newTenants = (await tenantsProvider.ListAsync(cancellationToken)).ToDictionary(x => x.Id.EmptyIfNull());
+ var tenantsFromProvider = (await tenantsProvider.ListAsync(cancellationToken)).ToList();
+ var newTenants = tenantsFromProvider.Count == 0
+ ? new Dictionary { [Tenant.DefaultTenantId] = Tenant.Default }
+ : tenantsFromProvider.ToDictionary(x => x.Id.EmptyIfNull());
var newTenantIds = newTenants.Keys;
var removedTenantIds = currentTenantIds.Except(newTenantIds).ToArray();
var addedTenantIds = newTenantIds.Except(currentTenantIds).ToArray();
@@ -112,7 +115,9 @@ public class DefaultTenantService(IServiceScopeFactory scopeFactory, ITenantScop
_tenantsDictionary = new Dictionary();
_tenantScopesDictionary = new Dictionary();
var tenantsProvider = _serviceScope.ServiceProvider.GetRequiredService();
- var tenants = await tenantsProvider.ListAsync(cancellationToken);
+ var tenants = (await tenantsProvider.ListAsync(cancellationToken)).ToList();
+ if (tenants.Count == 0)
+ tenants = [Tenant.Default];
foreach (var tenant in tenants)
await RegisterTenantAsync(tenant, cancellationToken);
diff --git a/test/unit/Elsa.Common.UnitTests/Multitenancy/DefaultTenantServiceTests.cs b/test/unit/Elsa.Common.UnitTests/Multitenancy/DefaultTenantServiceTests.cs
new file mode 100644
index 000000000..89685a0d5
--- /dev/null
+++ b/test/unit/Elsa.Common.UnitTests/Multitenancy/DefaultTenantServiceTests.cs
@@ -0,0 +1,145 @@
+using Elsa.Common.Multitenancy;
+using Elsa.Common.Multitenancy.EventHandlers;
+using Elsa.Common.RecurringTasks;
+using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+
+namespace Elsa.Common.UnitTests.Multitenancy;
+
+///
+/// Tests for , including the fallback to activate
+/// when the tenant provider returns an empty list.
+///
+public class DefaultTenantServiceTests
+{
+ [Fact]
+ public async Task ActivateTenantsAsync_WhenProviderReturnsEmpty_ActivatesDefaultTenant()
+ {
+ // Arrange - provider returns no tenants
+ var (tenantService, serviceProvider) = await CreateTenantServiceAsync(Array.Empty());
+
+ try
+ {
+ // Act
+ await tenantService.ActivateTenantsAsync();
+
+ // Assert
+ var tenants = (await tenantService.ListAsync()).ToList();
+ Assert.Single(tenants);
+ Assert.Same(Tenant.Default, tenants[0]);
+ Assert.Equal(Tenant.DefaultTenantId, tenants[0].Id);
+ }
+ finally
+ {
+ if (tenantService is IAsyncDisposable disposable)
+ await disposable.DisposeAsync();
+ await serviceProvider.DisposeAsync();
+ }
+ }
+
+ [Fact]
+ public async Task ListAsync_WhenProviderReturnsEmpty_ReturnsDefaultTenant()
+ {
+ // Arrange - ListAsync triggers initialization when provider returns empty
+ var (tenantService, serviceProvider) = await CreateTenantServiceAsync(Array.Empty());
+
+ try
+ {
+ // Act
+ var tenants = (await tenantService.ListAsync()).ToList();
+
+ // Assert
+ Assert.Single(tenants);
+ Assert.Same(Tenant.Default, tenants[0]);
+ }
+ finally
+ {
+ if (tenantService is IAsyncDisposable disposable)
+ await disposable.DisposeAsync();
+ await serviceProvider.DisposeAsync();
+ }
+ }
+
+ [Fact]
+ public async Task ActivateTenantsAsync_WhenProviderReturnsTenants_ReturnsThoseTenants()
+ {
+ // Arrange - provider returns specific tenants
+ var tenant1 = new Tenant { Id = "tenant-1", Name = "Tenant 1" };
+ var tenant2 = new Tenant { Id = "tenant-2", Name = "Tenant 2" };
+ var (tenantService, serviceProvider) = await CreateTenantServiceAsync([tenant1, tenant2]);
+
+ try
+ {
+ // Act
+ await tenantService.ActivateTenantsAsync();
+
+ // Assert - should not use Tenant.Default fallback
+ var tenants = (await tenantService.ListAsync()).ToList();
+ Assert.Equal(2, tenants.Count);
+ Assert.Contains(tenants, t => t.Id == "tenant-1");
+ Assert.Contains(tenants, t => t.Id == "tenant-2");
+ }
+ finally
+ {
+ if (tenantService is IAsyncDisposable disposable)
+ await disposable.DisposeAsync();
+ await serviceProvider.DisposeAsync();
+ }
+ }
+
+ [Fact]
+ public async Task RefreshAsync_WhenProviderChangesFromTenantsToEmpty_KeepsDefaultTenant()
+ {
+ // Arrange - start with tenants, then provider returns empty (simulating config change)
+ var tenant1 = new Tenant { Id = "tenant-1", Name = "Tenant 1" };
+ var providerReturns = new List { tenant1 };
+ var (tenantService, serviceProvider) = await CreateTenantServiceAsync(providerReturns, () => providerReturns);
+
+ try
+ {
+ await tenantService.ActivateTenantsAsync();
+ Assert.Single(await tenantService.ListAsync());
+
+ // Simulate provider now returning empty (e.g., config removed all tenants)
+ providerReturns.Clear();
+
+ // Act
+ await tenantService.RefreshAsync();
+
+ // Assert - should fall back to Tenant.Default instead of having zero tenants
+ var tenants = (await tenantService.ListAsync()).ToList();
+ Assert.Single(tenants);
+ Assert.Same(Tenant.Default, tenants[0]);
+ }
+ finally
+ {
+ if (tenantService is IAsyncDisposable disposable)
+ await disposable.DisposeAsync();
+ await serviceProvider.DisposeAsync();
+ }
+ }
+
+ private static Task<(ITenantService TenantService, ServiceProvider ServiceProvider)> CreateTenantServiceAsync(IEnumerable tenants, Func>? tenantsFactory = null)
+ {
+ var tenantList = tenants.ToList();
+ var getTenants = tenantsFactory ?? (() => tenantList);
+
+ var tenantsProvider = Substitute.For();
+ tenantsProvider.ListAsync(Arg.Any()).Returns(_ => getTenants());
+
+ var services = new ServiceCollection();
+ services.AddSingleton(_ => tenantsProvider);
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton(Substitute.For());
+ services.AddSingleton(Substitute.For());
+ services.AddSingleton(Substitute.For());
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddLogging();
+
+ var serviceProvider = services.BuildServiceProvider();
+ return Task.FromResult((serviceProvider.GetRequiredService(), serviceProvider));
+ }
+}