{"record":{"id":"75e9c3f559d9a905","repo":"elsa-workflows/elsa-core","slug":"the-workflow-definition-has-been-written-since-the-etag-in","errorCode":null,"errorMessage":"The workflow definition has been written since the ETag in If-Match was issued. GET the document again, reapply the edit, and PUT it with the new ETag.","messagePattern":"The workflow definition has been written since the ETag in If-Match was issued\\. GET the document again, reapply the edit, and PUT it with the new ETag\\.","errorType":"http","errorClass":"BpmnDocumentPreconditionFailedException","httpStatus":412,"severity":"error","filePath":"src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs","lineNumber":340,"sourceCode":"        string definitionId,\n        BpmnProcess process,\n        BpmnProcessDefinition rootDefinition,\n        BpmnImportAnalysis analysis,\n        string? expectedETag,\n        CancellationToken cancellationToken)\n    {\n        var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest).ToFilter();\n        var current = await store.FindAsync(filter, cancellationToken);\n\n        if (current is null)\n        {\n            throw new BpmnDefinitionNotFoundException(\n                $\"Workflow definition '{definitionId}' does not exist, so its BPMN document cannot be edited.\");\n        }\n\n        if (expectedETag is not null && !string.Equals(BpmnDocumentETag.From(current), expectedETag, StringComparison.Ordinal))\n        {\n            throw new BpmnDocumentPreconditionFailedException(\n                \"The workflow definition has been written since the ETag in If-Match was issued. GET the document again, reapply the edit, and PUT it with the new ETag.\");\n        }\n\n        var expectedId = current.Id;\n        var expectedVersion = current.Version;\n        var expectedName = current.Name;\n        var expectedDescription = current.Description;\n        var expectedStringData = current.StringData;\n\n        var draft = ApplyDocumentEdit(current, process, xml, rootDefinition);\n        await mediator.SendAsync(new WorkflowDefinitionDraftSaving(draft), cancellationToken);\n\n        var result = await store.TryUpdateLatestAsync(\n            filter,\n            loaded => loaded.IsLatest\n                      && loaded.Id == expectedId\n                      && loaded.Version == expectedVersion\n                      && loaded.StringData == expectedStringData","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs#L322-L358","documentation":"BpmnInterchangeDocumentService enforces optimistic concurrency: if an If-Match ETag was supplied but the stored definition's ETag (BpmnDocumentETag.From) no longer matches, it throws BpmnDocumentPreconditionFailedException. Someone else (or another process) wrote the definition after your ETag was issued, so your edit is rejected to avoid clobbering their changes.","triggerScenarios":"PersistDocumentEditAsync sees expectedETag non-null whose value differs from BpmnDocumentETag.From(current) — i.e. a concurrent write happened between your GET and your PUT.","commonSituations":"Two Studio sessions or two users editing the same BPMN document, an automation pipeline racing a manual edit, or retrying an old PUT after a previous successful write.","solutions":["GET the BPMN document again to obtain the fresh ETag, reapply your edit, and PUT with the new If-Match ETag","Retry with a short backoff if concurrent writers are expected and your change is idempotent","Use conditional logic to merge/verify changes before re-submitting"],"exampleFix":"// before\nawait client.PutAsync(url, content, new ETag(oldEtag)); // stale\n// after\nvar doc = await client.GetAsync(url); // returns fresh ETag\nreapplyEdit(doc);\nawait client.PutAsync(url, serialize(doc), new ETag(doc.Etag));","handlingStrategy":"retry","validationCode":"var doc = await getDocument(definitionId); // fresh ETag\nif (doc.Etag != expectedEtag) throw new ConcurrencyException(\"document changed; reapply edit\");","typeGuard":null,"tryCatchPattern":"try { await put(url, content, ifMatch: etag); }\ncatch (BpmnDocumentPreconditionFailedException) { var fresh = await get(url); reapply(fresh); await put(url, content, ifMatch: fresh.Etag); }","preventionTips":["Always GET immediately before PUT and use the returned ETag","Avoid long edit sessions between GET and PUT","Design merges rather than blind overwrites for shared documents"],"tags":["bpmn","optimistic-concurrency","etag","conflict"],"backgroundTag":"etag-precondition-failed","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"}