elsa-workflows/elsa-core · error · InvalidOperationException
A secret named ' ' already exists.
Error message
A secret named '{secret.Name}' already exists. What it means
Thrown by InMemorySecretRepository.AddAsync when a secret with the same name (case-insensitive) is already present in the in-memory dictionary. AddAsync uses ConcurrentDictionary.TryAdd, so any existing entry - including a previously deleted-but-present secret in some flows - causes this InvalidOperationException.
Solutions
- Reset or recreate the in-memory repository (or remove the existing entry) before adding in tests/startup code.
- Check for existence first and skip or update instead of adding.
- Use AddOrGetExistingAsync so an existing secret is returned rather than throwing.
- Make creation idempotent by catching InvalidOperationException and verifying the existing secret matches the desired state.
Example fix
// before
await inMemoryRepository.AddAsync(secret); // throws on rerun
// after
var existing = await manager.GetAsync(secret.Name);
if (existing == null)
await inMemoryRepository.AddAsync(secret); Defensive patterns
Strategy: validation
Validate before calling
if (await manager.GetAsync(secret.Name) != null) return; // already added
Try / catch
try { await inMemoryRepository.AddAsync(secret); }
catch (InvalidOperationException e) when (e.Message.Contains("already exists")) { /* reuse existing entry */ } Prevention
- Reset the in-memory repository between test cases.
- Use AddOrGetExistingAsync instead of AddAsync for tolerant seeding.
- Track already-provisioned secret names in startup code.
When it happens
Trigger: Calling InMemorySecretRepository.AddAsync with a Secret whose Name collides with an existing key in the dictionary; also reached via AddOrGetExistingAsync when the name already exists.
Common situations: Unit tests reusing a shared in-memory repository across test cases without resetting it, seeding the same secret name twice at startup, or retrying a creation call after a transient failure when the first attempt actually succeeded.
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/abb346f5adcb8422.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets/Repositories/InMemorySecretRepository.cs:23
public class InMemorySecretRepository : ISecretRepository
{
private readonly ConcurrentDictionary<string, Secret> _secrets = new(StringComparer.OrdinalIgnoreCase);
public Task<Secret?> GetAsync(string normalizedName, CancellationToken cancellationToken = default)
{
_secrets.TryGetValue(normalizedName, out var secret);
return Task.FromResult(secret == null ? null : Clone(secret));
}
public Task<IReadOnlyCollection<Secret>> ListAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult<IReadOnlyCollection<Secret>>(_secrets.Values.Select(Clone).ToList());
}
public Task AddAsync(Secret secret, CancellationToken cancellationToken = default)
{
if (!_secrets.TryAdd(secret.Name, Clone(secret)))
throw new InvalidOperationException($"A secret named '{secret.Name}' already exists.");
return Task.CompletedTask;
}
public Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)
{
var secretClone = Clone(secret);
while (true)
{
if (!_secrets.TryGetValue(secret.Name, out var existingSecret))
return Task.FromResult(_secrets.TryAdd(secret.Name, secretClone));
if (existingSecret.Status != SecretStatus.Deleted)
return Task.FromResult(false);
if (_secrets.TryUpdate(secret.Name, secretClone, existingSecret))
return Task.FromResult(true);View on GitHub (pinned to fe9217bdfa)