elsa-workflows/elsa-core · error · NotSupportedException

The VNext secrets persistence provider does not support…

Error message

The VNext secrets persistence provider does not support multitenancy, and the current tenant is '{tenantId}'. It stores secrets keyed by name alone, so tenants would share them. Use an Entity Framework Core secrets provider.

What it means

The VNext secrets repository stores secrets keyed only by normalized name with no tenant discriminator, so it cannot isolate data between tenants. EnsureDefaultTenant is called by every read/write operation and throws NotSupportedException whenever the ambient tenant accessor reports a non-empty tenant id, preventing silent cross-tenant secret sharing.

Solutions

  1. Switch to the Entity Framework Core secrets provider (Elsa.Secrets.Persistence.EFCore), which supports tenant-scoped secrets, and run its migrations.
  2. If secrets are intentionally global, run this workload in single-tenant mode so tenantAccessor.TenantId is null/empty.
  3. Remove or bypass tenant assignment for the secrets module only if sharing secrets across tenants is acceptable — generally not recommended.
  4. Audit configuration: ensure the DI registration matches the tenancy mode documented for the deployment.

Example fix

// before
services.AddSecretsFeature().UseVNextPersistence(); // with multitenancy enabled
// after
services.AddSecretsFeature().UseEntityFrameworkCorePersistence(); // tenant-aware store
Defensive patterns

Strategy: try-catch

Validate before calling

var tenantId = tenantAccessor?.TenantId;
if (!string.IsNullOrEmpty(tenantId) && usingVNextSecretsProvider)
    throw new InvalidOperationException("Configure the EF Core secrets provider; VNext secrets do not support multitenancy.");

Try / catch

try
{
    await repository.GetAsync(name, cancellationToken);
}
catch (NotSupportedException ex) when (ex.Message.Contains("does not support multitenancy"))
{
    logger.LogCritical(ex, "Secrets provider incompatible with multi-tenant mode; switch to EF Core provider.");
    throw;
}

Prevention

When it happens

Trigger: Any VNext repository operation (GetAsync, ListAsync, AddAsync, TryAddOrReplaceDeletedAsync, SaveAsync) executed in a multi-tenant host where the current request carries a tenant id — i.e. the app registered the VNext provider while tenancy is enabled.

Common situations: Enabling Elsa multitenancy after adopting the VNext secrets provider; a request scoped to a tenant (via header/token) hitting secrets APIs; environment misconfiguration where an EF Core secrets provider was intended but VNext was registered.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/3a48b91d5121fe20. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Secrets.Persistence.VNext/Repositories/VNextSecretRepository.cs:37

    /// the same document. Making it tenant-aware means changing the document id scheme, which relocates
    /// existing documents and is a storage change to make deliberately rather than fold into a tenancy fix.
    /// <para>
    /// Until then this throws rather than quietly serving one tenant's secret to another. It is checked per
    /// call rather than at startup so it catches a tenant context entered at runtime, and it stays silent for
    /// the default tenant, which is every single-tenant deployment.
    /// </para>
    /// <para>
    /// The accessor is optional because it is registered by the tenants module: a host that never added
    /// multitenancy has none, and requiring it would break resolving this repository at all. No accessor
    /// means no tenancy, which is the default tenant.
    /// </para>
    /// </remarks>
    private void EnsureDefaultTenant()
    {
        var tenantId = tenantAccessor?.TenantId;

        if (!string.IsNullOrEmpty(tenantId))
            throw new NotSupportedException(
                $"The VNext secrets persistence provider does not support multitenancy, and the current tenant is '{tenantId}'. "
                + "It stores secrets keyed by name alone, so tenants would share them. Use an Entity Framework Core secrets provider.");
    }

    public const string StorageUnitName = "Secrets";

    private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web)
    {
        Converters = { new JsonStringEnumConverter() }
    };

    public async Task<Secret?> GetAsync(string normalizedName, CancellationToken cancellationToken = default)
    {
        EnsureDefaultTenant();
        var document = await documentStore.LoadAsync(StorageUnitName, NormalizeDocumentId(normalizedName), cancellationToken);
        return document is null ? null : Deserialize(document);
    }

View on GitHub (pinned to fe9217bdfa)