elsa-workflows/elsa-core · error · InvalidOperationException
A secret named ' ' already exists.
Error message
A secret named '{secret.Name}' already exists. What it means
EFCoreSecretRepository.AddAsync normalizes the incoming secret's name and checks whether a secret with that normalized name already exists. If so, it throws this InvalidOperationException before touching the database, giving a friendly duplicate-name error (case-insensitive, per the validator's normalization).
Solutions
- Check existence first (ListAsync/GetAsync by name) and use SaveAsync/update instead of AddAsync when the secret already exists.
- Remove the existing secret if the new one should replace it, then AddAsync again.
- Pick a unique name for the new secret.
- Race conditions: catch InvalidOperationException from AddAsync/SaveChangesAsync and fall back to update semantics.
Example fix
// before
await repository.AddAsync(new Secret { Name = existingName });
// after
var existing = await repository.GetAsync(existingName, cancellationToken);
if (existing is null)
await repository.AddAsync(new Secret { Name = existingName });
else
existing.Value = newValue;
await repository.SaveAsync(existing, cancellationToken); Defensive patterns
Strategy: try-catch
Validate before calling
var existing = await repository.GetAsync(normalizedName, cancellationToken);
if (existing is not null)
throw new InvalidOperationException($"Secret '{normalizedName}' already exists; use SaveAsync to update."); Try / catch
try
{
await repository.AddAsync(secret, cancellationToken);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("A secret named '"))
{
logger.LogWarning(ex, "Secret '{Name}' already exists.", secret.Name);
// switch to update path or surface a duplicate-name message to the user
} Prevention
- Check existence by normalized name before AddAsync.
- Offer users 'create or update' semantics instead of blind AddAsync.
- Make seeding scripts idempotent.
When it happens
Trigger: Calling AddAsync with a secret whose Name matches (case-insensitively, after normalization) an existing secret row — e.g. adding "ApiKey" when "apikey" already exists.
Common situations: Retry logic re-adding a secret after a transient failure; seeding scripts that run twice; users typing a name that differs only by case from an existing secret; concurrent creation where both passed the check but only one insert wins (the other surfaces via SaveChangesAsync's variant of this error).
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
- A secret named ' ' already exists.
- A secret named ' ' already exists.
- A secret named ' ' already exists.
- A secret named ' ' already exists.
- A secret named ' ' already exists.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/12f5d9fb70f213df.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets.Persistence.EFCore/Repositories/EFCoreSecretRepository.cs:34
}
public async Task<IReadOnlyCollection<Secret>> ListAsync(CancellationToken cancellationToken = default)
{
await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
var secrets = await dbContext.Secrets.ToListAsync(cancellationToken);
foreach (var secret in secrets)
SecretSerialization.LoadSerializedProperties(dbContext, secret);
return secrets;
}
public async Task AddAsync(Secret secret, CancellationToken cancellationToken = default)
{
await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
var normalizedName = secretNameValidator.Normalize(secret.Name);
if (await ExistsByNormalizedNameAsync(dbContext, normalizedName, cancellationToken))
throw new InvalidOperationException($"A secret named '{secret.Name}' already exists.");
await dbContext.Secrets.AddAsync(secret, cancellationToken);
SetNormalizedName(dbContext, secret);
SecretSerialization.StoreSerializedProperties(dbContext, secret);
await SaveChangesAsync(dbContext, secret.Name, cancellationToken);
}
public async Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)
{
await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
var existingSecret = await FindByNameAsync(dbContext, secret.Name, cancellationToken);
if (existingSecret == null)
{
await dbContext.Secrets.AddAsync(secret, cancellationToken);
SetNormalizedName(dbContext, secret);
SecretSerialization.StoreSerializedProperties(dbContext, secret);
return await TrySaveChangesAsync(dbContext, secret.Name, cancellationToken);View on GitHub (pinned to fe9217bdfa)