elsa-workflows/elsa-core · error · BpmnDocumentPreconditionFailedException

The workflow definition has been written since the ETag in…

Error message

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.

What it means

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.

Solutions

  1. GET the BPMN document again to obtain the fresh ETag, reapply your edit, and PUT with the new If-Match ETag
  2. Retry with a short backoff if concurrent writers are expected and your change is idempotent
  3. Use conditional logic to merge/verify changes before re-submitting

Example fix

// before
await client.PutAsync(url, content, new ETag(oldEtag)); // stale
// after
var doc = await client.GetAsync(url); // returns fresh ETag
reapplyEdit(doc);
await client.PutAsync(url, serialize(doc), new ETag(doc.Etag));
Defensive patterns

Strategy: retry

Validate before calling

var doc = await getDocument(definitionId); // fresh ETag
if (doc.Etag != expectedEtag) throw new ConcurrencyException("document changed; reapply edit");

Try / catch

try { await put(url, content, ifMatch: etag); }
catch (BpmnDocumentPreconditionFailedException) { var fresh = await get(url); reapply(fresh); await put(url, content, ifMatch: fresh.Etag); }

Prevention

When it happens

Trigger: PersistDocumentEditAsync sees expectedETag non-null whose value differs from BpmnDocumentETag.From(current) — i.e. a concurrent write happened between your GET and your PUT.

Common situations: 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.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/75e9c3f559d9a905. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs:340

        string definitionId,
        BpmnProcess process,
        BpmnProcessDefinition rootDefinition,
        BpmnImportAnalysis analysis,
        string? expectedETag,
        CancellationToken cancellationToken)
    {
        var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest).ToFilter();
        var current = await store.FindAsync(filter, cancellationToken);

        if (current is null)
        {
            throw new BpmnDefinitionNotFoundException(
                $"Workflow definition '{definitionId}' does not exist, so its BPMN document cannot be edited.");
        }

        if (expectedETag is not null && !string.Equals(BpmnDocumentETag.From(current), expectedETag, StringComparison.Ordinal))
        {
            throw new BpmnDocumentPreconditionFailedException(
                "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.");
        }

        var expectedId = current.Id;
        var expectedVersion = current.Version;
        var expectedName = current.Name;
        var expectedDescription = current.Description;
        var expectedStringData = current.StringData;

        var draft = ApplyDocumentEdit(current, process, xml, rootDefinition);
        await mediator.SendAsync(new WorkflowDefinitionDraftSaving(draft), cancellationToken);

        var result = await store.TryUpdateLatestAsync(
            filter,
            loaded => loaded.IsLatest
                      && loaded.Id == expectedId
                      && loaded.Version == expectedVersion
                      && loaded.StringData == expectedStringData

View on GitHub (pinned to fe9217bdfa)