{"record":{"id":"e30cfb5a1e442665","repo":"elsa-workflows/elsa-core","slug":"a-workflow-definition-already-exists-for-definition","errorCode":null,"errorMessage":"A workflow definition already exists for definition '{definition.DefinitionId}' version {definition.Version} tenant '{definition.TenantId}'.","messagePattern":"A workflow definition already exists for definition '(.+?)' version (.+?) tenant '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs","lineNumber":217,"sourceCode":"    /// </remarks>\n    private IQueryable<WorkflowDefinition> Filter(IQueryable<WorkflowDefinition> queryable, WorkflowDefinitionFilter filter) =>\n        filter.Apply(queryable.WhereVisibleToTenant(CurrentTenantId, filter.TenantAgnostic));\n\n    private string CurrentTenantId => tenantAccessor?.TenantId ?? Tenant.DefaultTenantId;\n\n    /// <remarks>\n    /// EF enforces <c>(DefinitionId, Version)</c> globally via\n    /// <c>IX_WorkflowDefinition_DefinitionId_Version</c>. Memory keeps tenant in the key so\n    /// same-tenant duplicates fail closed while cross-tenant rows remain distinct until #7539\n    /// adds <c>TenantId</c> to that index.\n    /// </remarks>\n    private void EnsureVersionKeyAvailable(WorkflowDefinition definition)\n    {\n        var versionKey = GetVersionKey(definition);\n        var existing = store.Find(x => x.Id != definition.Id && GetVersionKey(x) == versionKey);\n\n        if (existing is not null)\n            throw CreateVersionKeyConflict(definition);\n    }\n\n    private static void EnsureBatchVersionKeysUnique(IEnumerable<WorkflowDefinition> definitions)\n    {\n        var seen = new Dictionary<DefinitionVersionKey, string>();\n\n        foreach (var definition in definitions)\n        {\n            var versionKey = GetVersionKey(definition);\n\n            if (seen.TryGetValue(versionKey, out var existingId) && existingId != definition.Id)\n                throw CreateVersionKeyConflict(definition);\n\n            seen[versionKey] = definition.Id;\n        }\n    }\n\n    private static InvalidOperationException CreateVersionKeyConflict(WorkflowDefinition definition) =>","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs#L199-L235","documentation":"The in-memory workflow definition store enforces uniqueness of (DefinitionId, Version, TenantId) across separately-stored definitions. Before saving, EnsureVersionKeyAvailable checks that no other stored definition (different Id) occupies the same version key; if one does, saving would silently shadow a version, so a conflict exception is thrown.","triggerScenarios":"Calling SaveAsync (or SaveManyAsync) with a WorkflowDefinition whose Id differs from an existing stored item but whose (DefinitionId, Version, TenantId) triple matches that existing item — e.g. re-importing a definition version with a fresh Id.","commonSituations":"Re-publishing/importing workflow definitions without bumping Version; duplicate imports from JSON/JSON files with regenerated Ids; multi-tenant data saved with the wrong TenantId; concurrent saves of the same version from two hosts sharing the in-memory store in dev.","solutions":["Bump the definition's Version (or let the versioning service increment it) before saving instead of reusing an existing version number.","Load the existing definition by Id and update it, rather than saving a new WorkflowDefinition object with the same definition/version key.","Delete the conflicting version first if it is truly obsolete, then save the new one.","Correct the TenantId if the collision is only apparent because definitions were attributed to the wrong tenant.","Generate and reuse a stable Id per definition version on import so updates map to updates, not inserts."],"exampleFix":"// before\nvar copy = existing with { Id = Guid.NewGuid().ToString() }; // same DefinitionId+Version\nawait store.SaveAsync(copy); // conflict\n// after\nvar copy = existing with { Id = Guid.NewGuid().ToString(), Version = existing.Version + 1 };\nawait store.SaveAsync(copy);","handlingStrategy":"validation","validationCode":"var existing = await store.FindAsync(new WorkflowDefinitionFilter { DefinitionId = definition.DefinitionId, Version = definition.Version });\nif (existing != null && existing.Id != definition.Id) throw new InvalidOperationException(\"Version key already used by a different record; bump Version or update the existing record\");","typeGuard":null,"tryCatchPattern":"try { await store.SaveAsync(definition); } catch (Exception ex) { logger.LogError(ex, \"Version conflict for {DefinitionId} v{Version}\", definition.DefinitionId, definition.Version); }","preventionTips":["Increment Version for each new version instead of reusing numbers","Save updates against the existing record's Id rather than inserting duplicates","Preserve Ids during imports so re-import is an update, not a new record"],"tags":["persistence","workflow-definitions","duplicate"],"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"}