{"record":{"id":"780b4380d4f677f2","repo":"elsa-workflows/elsa-core","slug":"a-secret-named-secret-name-already-exists-780b43","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.Persistence.VNext/Repositories/VNextSecretRepository.cs","lineNumber":81,"sourceCode":"                new DocumentQuery(StorageUnitName, new Dictionary<string, string?> { [nameof(Secret.Status)] = status.ToString() }),\n                cancellationToken);\n\n            results.AddRange(documents.Select(Deserialize));\n        }\n\n        return results.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList();\n    }\n\n    public async Task AddAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        EnsureDefaultTenant();\n        try\n        {\n            await SaveAsync(secret, expectedVersion: 0, cancellationToken);\n        }\n        catch (DocumentStoreConcurrencyException)\n        {\n            throw new InvalidOperationException($\"A secret named '{secret.Name}' already exists.\");\n        }\n    }\n\n    public async Task<bool> TryAddOrReplaceDeletedAsync(Secret secret, CancellationToken cancellationToken = default)\n    {\n        EnsureDefaultTenant();\n        while (true)\n        {\n            var existing = await LoadDocumentAsync(secret.Name, cancellationToken);\n            if (existing?.Secret.Status is not null and not SecretStatus.Deleted)\n                return false;\n\n            try\n            {\n                await SaveAsync(secret, existing?.Document.Version ?? 0, cancellationToken);\n                return true;\n            }\n            catch (DocumentStoreConcurrencyException)","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Secrets.Persistence.VNext/Repositories/VNextSecretRepository.cs#L63-L99","documentation":"VNextSecretRepository.AddAsync saves a new secret with expectedVersion 0, so the underlying document store rejects the write if a document with the same name index already exists (DocumentStoreConcurrencyException). The repository translates that into this InvalidOperationException. It functions as a duplicate-name guard for secret creation.","triggerScenarios":"Calling AddAsync (via ISecretRepository.AddAsync) with a Secret whose Name matches an existing secret in the default tenant; two concurrent AddAsync calls racing to create the same secret name.","commonSituations":"Workflow setup code or a provisioning script creating secrets on every run without an existence check; retry logic re-running a creation step after a partial failure; two environments/instances racing to seed the same secret.","solutions":["Check existence first (e.g. query the repository by name) or use an upsert-style API instead of AddAsync before creating the secret.","Catch InvalidOperationException (or the underlying DocumentStoreConcurrencyException) around AddAsync and treat it as 'already created'.","Rename the new secret if a distinct secret was intended; names are unique per tenant.","Serialize creation through a single worker/job if concurrent seeding is the cause."],"exampleFix":"// before\nawait secretRepository.AddAsync(new Secret { Name = \"api-key\", ... }, ct);\n\n// after\nvar existing = await secretRepository.FindAsync(s => s.Name == \"api-key\", ct);\nif (existing is null)\n    await secretRepository.AddAsync(new Secret { Name = \"api-key\", ... }, ct);","handlingStrategy":"try-catch","validationCode":"var existing = await secretRepository.FindAsync(s => s.Name == secret.Name, cancellationToken);\nif (existing is not null) throw new InvalidOperationException($\"Secret '{secret.Name}' already exists.\");","typeGuard":null,"tryCatchPattern":"try { await secretRepository.AddAsync(secret, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already exists\")) { /* handle duplicate: load or skip */ }","preventionTips":["Always check for an existing secret by name before AddAsync.","Make secret provisioning idempotent (create-if-missing).","Use unique names that include environment or tenant scope."],"tags":["secrets","duplicate","concurrency","persistence"],"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"}