elsa-workflows/elsa-core · error · InvalidOperationException

A secret named ' ' already exists.

Error message

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

What it means

Thrown by DefaultSecretManager.CreateAsync when the repository refuses to add the newly created secret because an active (non-deleted) secret with the same name already exists. CreateAsync is create-only; rotating or updating an existing secret requires the dedicated APIs.

Solutions

  1. Call manager.GetAsync(name) first; if a secret exists, use RotateAsync or UpdateAsync instead of CreateAsync.
  2. Catch InvalidOperationException from CreateAsync and fall back to the update/rotate path.
  3. Generate unique names or include a scope/environment prefix to avoid collisions.
  4. Make provisioning scripts idempotent (create-if-missing).

Example fix

// before
await manager.CreateAsync(new CreateSecretRequest { Name = "db-password", Value = v });
// after
if (await manager.GetAsync("db-password") is null)
    await manager.CreateAsync(new CreateSecretRequest { Name = "db-password", Value = v });
else
    await manager.RotateAsync("db-password", new RotateSecretRequest { Value = v });
Defensive patterns

Strategy: try-catch

Validate before calling

if (await manager.GetAsync(request.Name) != null) throw new InvalidOperationException($"Secret '{request.Name}' exists; use rotate/update.");

Try / catch

try { await manager.CreateAsync(request); }
catch (InvalidOperationException e) when (e.Message.Contains("already exists")) { await manager.RotateAsync(request.Name, rotateFrom(request)); }

Prevention

When it happens

Trigger: Calling ISecretManager.CreateAsync(new CreateSecretRequest { Name = "existing-name", ... }) when a secret with that name (case-insensitive) already exists and TryAddOrReplaceDeletedAsync returns false.

Common situations: Re-running secret provisioning after a previous successful run, users submitting a create form for a name that already exists, or automated pipelines without idempotency.

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/9f88ff5ee3dca420. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Secrets/Services/DefaultSecretManager.cs:10

namespace Elsa.Secrets.Services;

public class DefaultSecretManager(ISecretNameValidator nameValidator, ISecretStoreRegistry storeRegistry, ISecretTypeRegistry typeRegistry, ISecretRepository repository) : ISecretManager
{
    public async Task<Secret> CreateAsync(CreateSecretRequest request, CancellationToken cancellationToken = default)
    {
        ValidateName(request.Name);
        var secret = await CreateSecretAsync(request, cancellationToken);
        if (!await repository.TryAddOrReplaceDeletedAsync(secret, cancellationToken))
            throw new InvalidOperationException($"A secret named '{request.Name}' already exists.");

        return secret;
    }

    public async Task<Secret?> GetAsync(string name, CancellationToken cancellationToken = default)
    {
        var secret = await repository.GetAsync(nameValidator.Normalize(name), cancellationToken);
        return secret is { Status: SecretStatus.Deleted } ? null : secret;
    }

    public async Task<IReadOnlyCollection<Secret>> ListAsync(ListSecretsRequest request, CancellationToken cancellationToken = default)
    {
        return (await ListPageAsync(request, cancellationToken)).Items;
    }

    public async Task<ListSecretsResult> ListPageAsync(ListSecretsRequest request, CancellationToken cancellationToken = default)
    {
        var secrets = await repository.ListAsync(cancellationToken);

View on GitHub (pinned to fe9217bdfa)