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 FileSecretRepository.AddAsync when a secret with the same name (case-insensitive) already exists in the file-backed store. AddAsync only creates new secrets; it never updates, so duplicate names are rejected as InvalidOperationException.
Solutions
- Check existence first with repository/manager GetAsync(name) and update (SaveAsync) instead of AddAsync when it exists.
- Use AddOrGetExistingAsync, which tolerates existing secrets and returns the existing one.
- Catch InvalidOperationException and treat the existing secret as the result (upsert semantics in caller code).
- Add idempotency to seeding scripts (skip names already present).
Example fix
// before
await repository.AddAsync(new Secret { Name = "api-key", ... });
// after
var existing = await manager.GetAsync("api-key");
if (existing == null)
await manager.CreateAsync(new CreateSecretRequest { Name = "api-key", ... });
else
await manager.UpdateAsync("api-key", new UpdateSecretRequest { ... }); Defensive patterns
Strategy: validation
Validate before calling
var existing = await manager.GetAsync(name); if (existing != null) /* update/rotate instead of add */;
Try / catch
try { await repository.AddAsync(secret); }
catch (InvalidOperationException e) when (e.Message.Contains("already exists")) { /* fetch existing and update instead */ } Prevention
- Use AddOrGetExistingAsync for idempotent creation.
- Make seed scripts check existence before adding.
- Standardize name normalization (case-insensitivity) across environments.
When it happens
Trigger: Calling ISecretRepository.AddAsync (directly or via AddOrGetExistingAsync) with a Secret whose Name matches an existing secret in the file store, ignoring case.
Common situations: Re-running an initialization/seed script that adds secrets without checking existence, concurrent provisioning racing to create the same secret, or retry logic re-submitting a creation that already 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/1db74b47c5269bb3.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets/Repositories/FileSecretRepository.cs:35
public async Task<Secret?> GetAsync(string normalizedName, CancellationToken cancellationToken = default)
{
var secrets = await ReadAllAsync(cancellationToken);
return secrets.FirstOrDefault(x => string.Equals(x.Name, normalizedName, StringComparison.OrdinalIgnoreCase));
}
public async Task<IReadOnlyCollection<Secret>> ListAsync(CancellationToken cancellationToken = default)
{
return await ReadAllAsync(cancellationToken);
}
public async Task AddAsync(Secret secret, CancellationToken cancellationToken = default)
{
await _lock.WaitAsync(cancellationToken);
try
{
var secrets = await ReadAllUnsafeAsync(cancellationToken);
if (secrets.Any(x => string.Equals(x.Name, secret.Name, StringComparison.OrdinalIgnoreCase)))
throw new InvalidOperationException($"A secret named '{secret.Name}' already exists.");
secrets.Add(secret);
await WriteAllUnsafeAsync(secrets, cancellationToken);
}
finally
{
_lock.Release();
}
}
public async Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)
{
await _lock.WaitAsync(cancellationToken);
try
{
var secrets = await ReadAllUnsafeAsync(cancellationToken);
var index = secrets.FindIndex(x => string.Equals(x.Name, secret.Name, StringComparison.OrdinalIgnoreCase));
if (index >= 0)View on GitHub (pinned to fe9217bdfa)