{"record":{"id":"cbcb55e9c404352c","repo":"elsa-workflows/elsa-core","slug":"a-bookmark-queue-dead-letter-item-for-original-queue-item","errorCode":null,"errorMessage":"A bookmark queue dead-letter item for original queue item '{record.OriginalQueueItemId}' already exists.","messagePattern":"A bookmark queue dead-letter item for original queue item '(.+?)' already exists\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Runtime/Stores/MemoryBookmarkQueueDeadLetterStore.cs","lineNumber":25,"sourceCode":"using Elsa.Workflows.Runtime.Options;\nusing JetBrains.Annotations;\n\nnamespace Elsa.Workflows.Runtime.Stores;\n\n/// <inheritdoc />\n[UsedImplicitly]\npublic class MemoryBookmarkQueueDeadLetterStore(MemoryStore<BookmarkQueueDeadLetterItem> store) : IBookmarkQueueDeadLetterStore\n{\n    private readonly object _lock = new();\n\n    /// <inheritdoc />\n    public Task SaveAsync(BookmarkQueueDeadLetterItem record, CancellationToken cancellationToken = default)\n    {\n        lock (_lock)\n        {\n            var existing = store.Find(x => x.OriginalQueueItemId == record.OriginalQueueItemId && x.Id != record.Id);\n            if (existing != null)\n                throw new InvalidOperationException($\"A bookmark queue dead-letter item for original queue item '{record.OriginalQueueItemId}' already exists.\");\n\n            store.Save(Clone(record), x => x.Id);\n        }\n\n        return Task.CompletedTask;\n    }\n\n    /// <inheritdoc />\n    public async Task AddAsync(BookmarkQueueDeadLetterItem record, CancellationToken cancellationToken = default)\n    {\n        await AddOrGetExistingAsync(record, cancellationToken);\n    }\n\n    /// <inheritdoc />\n    public Task<BookmarkQueueDeadLetterItem> AddOrGetExistingAsync(BookmarkQueueDeadLetterItem record, CancellationToken cancellationToken = default)\n    {\n        lock (_lock)\n        {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Runtime/Stores/MemoryBookmarkQueueDeadLetterStore.cs#L7-L43","documentation":"MemoryBookmarkQueueDeadLetterStore.SaveAsync enforces that at most one dead-letter item exists per OriginalQueueItemId. If a record with the same OriginalQueueItemId (and a different Id) already exists, it throws this InvalidOperationException instead of creating a duplicate.","triggerScenarios":"Calling SaveAsync on the in-memory dead-letter store (directly or via the dead-letter service) with a BookmarkQueueDeadLetterItem whose OriginalQueueItemId matches an existing dead-letter record but whose own Id differs.","commonSituations":"Re-enqueueing then re-dead-lettering the same queue item without reusing/replacing the existing dead-letter record; seeding test data that duplicates an original queue item id; a retry flow that creates a new item object instead of updating the existing one.","solutions":["Before saving, query the store for an existing item with the same OriginalQueueItemId and update/replace it (keeping its Id) instead of inserting a new record.","Reuse the existing dead-letter record's Id when creating the new item so the uniqueness check passes.","Delete the existing dead-letter item first if the new record should supersede it.","This duplicate check is specific to the in-memory store; if you see it, a production database provider may behave differently — verify which store implementation is registered."],"exampleFix":"// before\nvar item = new BookmarkQueueDeadLetterItem { OriginalQueueItemId = queueItemId, /* ... */ };\nawait deadLetterStore.SaveAsync(item, ct);\n// after\nvar existing = await deadLetterStore.FindByOriginalQueueItemIdAsync(queueItemId, ct);\nif (existing != null) { existing.Timestamp = DateTimeOffset.UtcNow; await deadLetterStore.SaveAsync(existing, ct); }\nelse await deadLetterStore.SaveAsync(item, ct);","handlingStrategy":"validation","validationCode":"var existing = await deadLetterStore.ListAsync(ct);\nbool dup = existing.Any(x => x.OriginalQueueItemId == item.OriginalQueueItemId && x.Id != item.Id);\nif (dup) throw new InvalidOperationException(\"Duplicate dead-letter item for \" + item.OriginalQueueItemId);","typeGuard":null,"tryCatchPattern":"try { await store.SaveAsync(record, ct); }\ncatch (InvalidOperationException) { /* fetch existing by OriginalQueueItemId and update it instead */ }","preventionTips":["Always look up by OriginalQueueItemId before inserting a dead-letter item.","Reuse the existing record's Id when re-dead-lettering the same queue item.","Treat dead-letter insertion as upsert semantics in your service layer."],"tags":["duplicate-record","in-memory-store","dead-letter","uniqueness-constraint"],"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-23T08:17:48.524Z"}