{"record":{"id":"abb346f5adcb8422","repo":"elsa-workflows/elsa-core","slug":"a-secret-named-secret-name-already-exists-abb346","errorCode":null,"errorMessage":"A secret named '{secret.Name}' already exists.","messagePattern":"A secret named '(.+?)' already exists\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Secrets/Repositories/InMemorySecretRepository.cs","lineNumber":23,"sourceCode":"public class InMemorySecretRepository : ISecretRepository\n{\n    private readonly ConcurrentDictionary<string, Secret> _secrets = new(StringComparer.OrdinalIgnoreCase);\n\n    public Task<Secret?> GetAsync(string normalizedName, CancellationToken cancellationToken = default)\n    {\n        _secrets.TryGetValue(normalizedName, out var secret);\n        return Task.FromResult(secret == null ? null : Clone(secret));\n    }\n\n    public Task<IReadOnlyCollection<Secret>> ListAsync(CancellationToken cancellationToken = default)\n    {\n        return Task.FromResult<IReadOnlyCollection<Secret>>(_secrets.Values.Select(Clone).ToList());\n    }\n\n    public Task AddAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        if (!_secrets.TryAdd(secret.Name, Clone(secret)))\n            throw new InvalidOperationException($\"A secret named '{secret.Name}' already exists.\");\n\n        return Task.CompletedTask;\n    }\n\n    public Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        var secretClone = Clone(secret);\n\n        while (true)\n        {\n            if (!_secrets.TryGetValue(secret.Name, out var existingSecret))\n                return Task.FromResult(_secrets.TryAdd(secret.Name, secretClone));\n\n            if (existingSecret.Status != SecretStatus.Deleted)\n                return Task.FromResult(false);\n\n            if (_secrets.TryUpdate(secret.Name, secretClone, existingSecret))\n                return Task.FromResult(true);","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Secrets/Repositories/InMemorySecretRepository.cs#L5-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait inMemoryRepository.AddAsync(secret); // throws on rerun\n// after\nvar existing = await manager.GetAsync(secret.Name);\nif (existing == null)\n    await inMemoryRepository.AddAsync(secret);","handlingStrategy":"validation","validationCode":"if (await manager.GetAsync(secret.Name) != null) return; // already added","typeGuard":null,"tryCatchPattern":"try { await inMemoryRepository.AddAsync(secret); }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"already exists\")) { /* reuse existing entry */ }","preventionTips":["Reset the in-memory repository between test cases.","Use AddOrGetExistingAsync instead of AddAsync for tolerant seeding.","Track already-provisioned secret names in startup code."],"tags":["secrets","duplicate","conflict","in-memory"],"backgroundTag":"file-already-exists","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}