{"record":{"id":"1db74b47c5269bb3","repo":"elsa-workflows/elsa-core","slug":"a-secret-named-secret-name-already-exists-1db74b","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/FileSecretRepository.cs","lineNumber":35,"sourceCode":"    public async Task<Secret?> GetAsync(string normalizedName, CancellationToken cancellationToken = default)\n    {\n        var secrets = await ReadAllAsync(cancellationToken);\n        return secrets.FirstOrDefault(x => string.Equals(x.Name, normalizedName, StringComparison.OrdinalIgnoreCase));\n    }\n\n    public async Task<IReadOnlyCollection<Secret>> ListAsync(CancellationToken cancellationToken = default)\n    {\n        return await ReadAllAsync(cancellationToken);\n    }\n\n    public async Task AddAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        await _lock.WaitAsync(cancellationToken);\n        try\n        {\n            var secrets = await ReadAllUnsafeAsync(cancellationToken);\n            if (secrets.Any(x => string.Equals(x.Name, secret.Name, StringComparison.OrdinalIgnoreCase)))\n                throw new InvalidOperationException($\"A secret named '{secret.Name}' already exists.\");\n\n            secrets.Add(secret);\n            await WriteAllUnsafeAsync(secrets, cancellationToken);\n        }\n        finally\n        {\n            _lock.Release();\n        }\n    }\n\n    public async Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        await _lock.WaitAsync(cancellationToken);\n        try\n        {\n            var secrets = await ReadAllUnsafeAsync(cancellationToken);\n            var index = secrets.FindIndex(x => string.Equals(x.Name, secret.Name, StringComparison.OrdinalIgnoreCase));\n            if (index >= 0)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Secrets/Repositories/FileSecretRepository.cs#L17-L53","documentation":"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.","triggerScenarios":"Calling ISecretRepository.AddAsync (directly or via AddOrGetExistingAsync) with a Secret whose Name matches an existing secret in the file store, ignoring case.","commonSituations":"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.","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)."],"exampleFix":"// before\nawait repository.AddAsync(new Secret { Name = \"api-key\", ... });\n// after\nvar existing = await manager.GetAsync(\"api-key\");\nif (existing == null)\n    await manager.CreateAsync(new CreateSecretRequest { Name = \"api-key\", ... });\nelse\n    await manager.UpdateAsync(\"api-key\", new UpdateSecretRequest { ... });","handlingStrategy":"validation","validationCode":"var existing = await manager.GetAsync(name);\nif (existing != null) /* update/rotate instead of add */;","typeGuard":null,"tryCatchPattern":"try { await repository.AddAsync(secret); }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"already exists\")) { /* fetch existing and update instead */ }","preventionTips":["Use AddOrGetExistingAsync for idempotent creation.","Make seed scripts check existence before adding.","Standardize name normalization (case-insensitivity) across environments."],"tags":["secrets","duplicate","conflict","repository"],"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"}