{"record":{"id":"316a0f9661132d87","repo":"elsa-workflows/elsa-core","slug":"document-documentid-in-storage-unit-storageunit-expected-316a0f","errorCode":null,"errorMessage":"Document '{documentId}' in storage unit '{storageUnit}' expected version '{expectedVersion?.ToString() ?? \"<none>\"}' but actual version was '{actual?.Version?.ToString() ?? \"<none>\"}'.","messagePattern":"Document '(.+?)' in storage unit '(.+?)' expected version '(.+?)' but actual version was '(.+?)'\\.","errorType":"exception","errorClass":"DocumentStoreConcurrencyException","httpStatus":409,"severity":"error","filePath":"src/modules/Elsa.Persistence.VNext.MongoDb/MongoDbDocumentStore.cs","lineNumber":206,"sourceCode":"\n    private static FilterDefinition<BsonDocument> CreateDeleteFilter(string id, long? expectedVersion)\n    {\n        var filter = Builders<BsonDocument>.Filter.Eq(\"_id\", id);\n        return expectedVersion is null ? filter : Builders<BsonDocument>.Filter.And(filter, Builders<BsonDocument>.Filter.Eq(\"Version\", expectedVersion.Value));\n    }\n\n    private static bool DidExpectedWriteSucceed(ReplaceOneResult result)\n    {\n        if (!result.IsAcknowledged)\n            return true;\n\n        return result.MatchedCount > 0;\n    }\n\n    private static async Task ThrowConcurrencyExceptionAsync(IMongoCollection<BsonDocument> collection, string storageUnit, string documentId, long expectedVersion, CancellationToken cancellationToken)\n    {\n        var actual = await LoadDocumentAsync(collection, storageUnit, documentId, cancellationToken);\n        throw new DocumentStoreConcurrencyException(storageUnit, documentId, expectedVersion, actual?.Version);\n    }\n\n    private static BsonDocument CreateDocument(StoredDocument document, IReadOnlyDictionary<string, string?> indexValues)\n    {\n        return new BsonDocument\n        {\n            [\"_id\"] = document.Id,\n            [\"Content\"] = document.Content,\n            [\"Version\"] = document.Version,\n            [\"CreatedAt\"] = new BsonDateTime(document.CreatedAt.UtcDateTime),\n            [\"UpdatedAt\"] = new BsonDateTime(document.UpdatedAt.UtcDateTime),\n            [\"Data\"] = ParseContent(document.Content),\n            [\"IndexValues\"] = new BsonDocument(indexValues.Select(x => new BsonElement(x.Key, x.Value is null ? BsonNull.Value : BsonValue.Create(x.Value))))\n        };\n    }\n\n    private static BsonValue ParseContent(string content)\n    {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Persistence.VNext.MongoDb/MongoDbDocumentStore.cs#L188-L224","documentation":"This is the failure detail thrown after an optimistic-concurrency update fails at the MongoDB level (e.g. a FindOneAndUpdate/ReplaceOne with a version filter matched nothing). ThrowConcurrencyExceptionAsync reloads the document to report its actual version (or <none> if it was deleted) inside DocumentStoreConcurrencyException, giving you the expected vs actual versions.","triggerScenarios":"SaveAsync or DeleteAsync attempted a version-guarded write whose filter matched no documents — another writer changed or deleted the document between your read and write, or the document never existed at the expected version.","commonSituations":"Race between two concurrent saves where the second overwrite is rejected; a delete racing a save; retrying a queued operation after the document was already updated or removed.","solutions":["Reload the document, merge your changes with its current state, and retry the save with the new actual version as expectedVersion.","Catch DocumentStoreConcurrencyException and apply a bounded retry loop (re-read, re-apply, re-save).","If the message shows actual version '<none>', the document was deleted — decide whether to recreate it or skip the write."],"exampleFix":"// before\nawait store.SaveAsync(collection, request with { ExpectedVersion = 7 }, ct); // throws: actual was 8\n\n// after\ntry {\n    await store.SaveAsync(collection, request with { ExpectedVersion = 7 }, ct);\n} catch (DocumentStoreConcurrencyException) {\n    var current = await store.LoadAsync(collection, request.Id, ct);\n    await store.SaveAsync(collection, request with { ExpectedVersion = current?.Version }, ct);\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for (var attempt = 0; attempt < 3; attempt++)\n{\n    try\n    {\n        await store.SaveAsync(collection, request, ct);\n        break;\n    }\n    catch (DocumentStoreConcurrencyException) when (attempt < 2)\n    {\n        var current = await store.LoadAsync(collection, request.Id, ct);\n        request = request with { ExpectedVersion = current?.Version };\n    }\n}","preventionTips":["Treat this exception as a normal race outcome: catch it, reload, and retry rather than failing the operation.","If the message reports actual version '<none>', handle the deleted-document case explicitly.","Serialize writes for the same document through a single logical owner (e.g. per-document lock) in high-contention scenarios."],"tags":["mongodb","concurrency","optimistic-locking","persistence"],"backgroundTag":"invalid-state-transition","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}