elsa-workflows/elsa-core · error · InvalidOperationException

A secret named ' ' already exists.

Error message

A secret named '{secret.Name}' already exists.

What it means

VNextSecretRepository.AddAsync saves a new secret with expectedVersion 0, so the underlying document store rejects the write if a document with the same name index already exists (DocumentStoreConcurrencyException). The repository translates that into this InvalidOperationException. It functions as a duplicate-name guard for secret creation.

Solutions

  1. Check existence first (e.g. query the repository by name) or use an upsert-style API instead of AddAsync before creating the secret.
  2. Catch InvalidOperationException (or the underlying DocumentStoreConcurrencyException) around AddAsync and treat it as 'already created'.
  3. Rename the new secret if a distinct secret was intended; names are unique per tenant.
  4. Serialize creation through a single worker/job if concurrent seeding is the cause.

Example fix

// before
await secretRepository.AddAsync(new Secret { Name = "api-key", ... }, ct);

// after
var existing = await secretRepository.FindAsync(s => s.Name == "api-key", ct);
if (existing is null)
    await secretRepository.AddAsync(new Secret { Name = "api-key", ... }, ct);
Defensive patterns

Strategy: try-catch

Validate before calling

var existing = await secretRepository.FindAsync(s => s.Name == secret.Name, cancellationToken);
if (existing is not null) throw new InvalidOperationException($"Secret '{secret.Name}' already exists.");

Try / catch

try { await secretRepository.AddAsync(secret, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already exists")) { /* handle duplicate: load or skip */ }

Prevention

When it happens

Trigger: Calling AddAsync (via ISecretRepository.AddAsync) with a Secret whose Name matches an existing secret in the default tenant; two concurrent AddAsync calls racing to create the same secret name.

Common situations: Workflow setup code or a provisioning script creating secrets on every run without an existence check; retry logic re-running a creation step after a partial failure; two environments/instances racing to seed the same secret.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

                new DocumentQuery(StorageUnitName, new Dictionary<string, string?> { [nameof(Secret.Status)] = status.ToString() }),
                cancellationToken);

            results.AddRange(documents.Select(Deserialize));
        }

        return results.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList();
    }

    public async Task AddAsync(Secret secret, CancellationToken cancellationToken = default)
    {
        EnsureDefaultTenant();
        try
        {
            await SaveAsync(secret, expectedVersion: 0, cancellationToken);
        }
        catch (DocumentStoreConcurrencyException)
        {
            throw new InvalidOperationException($"A secret named '{secret.Name}' already exists.");
        }
    }

    public async Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)
    {
        EnsureDefaultTenant();
        while (true)
        {
            var existing = await LoadDocumentAsync(secret.Name, cancellationToken);
            if (existing?.Secret.Status is not null and not SecretStatus.Deleted)
                return false;

            try
            {
                await SaveAsync(secret, existing?.Document.Version ?? 0, cancellationToken);
                return true;
            }
            catch (DocumentStoreConcurrencyException)

View on GitHub (pinned to fe9217bdfa)