{"record":{"id":"3e3a294561576854","repo":"elsa-workflows/elsa-core","slug":"a-label-already-exists-with-normalized-name-record","errorCode":null,"errorMessage":"A label already exists with normalized name '{record.NormalizedName}' in tenant '{record.TenantId}'.","messagePattern":"A label already exists with normalized name '(.+?)' in tenant '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Labels/Services/InMemoryLabelStore.cs","lineNumber":178,"sourceCode":"    private static void SyncNormalizedName(Label record) =>\n        record.NormalizedName = record.Name.ToLowerInvariant();\n\n    /// <summary>\n    /// Memory counterpart of the EF unique index on <c>(TenantId, NormalizedName)</c>.\n    /// Same-Id upserts are allowed so a row can rename itself. Incoming batch rows\n    /// replace same-Id store rows, so those store rows are ignored here.\n    /// </summary>\n    private void EnsureNormalizedNameAvailable(Label record, IReadOnlyCollection<Label> batch)\n    {\n        var batchIds = batch.Select(x => x.Id).ToHashSet();\n        var existing = _labelStore.Find(candidate =>\n            candidate.TenantId == record.TenantId\n            && candidate.Id != record.Id\n            && !batchIds.Contains(candidate.Id)\n            && candidate.NormalizedName == record.NormalizedName);\n\n        if (existing is not null)\n            throw DuplicateNormalizedName(record);\n\n        if (batch.Any(other =>\n                other.Id != record.Id\n                && other.TenantId == record.TenantId\n                && other.NormalizedName == record.NormalizedName))\n        {\n            throw DuplicateNormalizedName(record);\n        }\n    }\n\n    private static InvalidOperationException DuplicateNormalizedName(Label record) =>\n        new($\"A label already exists with normalized name '{record.NormalizedName}' in tenant '{record.TenantId}'.\");\n}\n","sourceCodeStart":160,"sourceCodeEnd":192,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Labels/Services/InMemoryLabelStore.cs#L160-L192","documentation":"InMemoryLabelStore enforces that each label has a unique NormalizedName per tenant. EnsureNormalizedNameAvailable is called by SaveAsync/SaveManyAsync and throws a DuplicateNormalizedName exception when another stored label (or another record in the same save batch) already uses the same normalized name in the same tenant. Normalization makes names case/format-insensitive, so 'Bug' and 'bug' collide.","triggerScenarios":"Creating or updating a label whose NormalizedName equals an existing label's in the same tenant (excluding the record itself); also saving multiple labels in one SaveManyAsync call where two records normalize to the same name.","commonSituations":"Importing label seeds or fixtures that contain duplicates; API clients posting labels whose names differ only by case/whitespace; multi-tenant setups where tenantId is accidentally identical or empty for all records.","solutions":["Rename the new label so its normalized name is unique within the tenant","Check existing labels (e.g. via the labels API) before creating one and handle duplicates in your code","Ensure distinct TenantId values are set correctly for tenants, since uniqueness is per-tenant","When using SaveManyAsync, de-duplicate the batch by NormalizedName before saving"],"exampleFix":"// before\nawait store.SaveAsync(new Label { Name = \"Production\" }, cancellationToken); // 'production' already exists\n// after\nvar existing = (await store.ListAsync(cancellationToken)).Any(l => l.NormalizedName == \"production\");\nif (!existing) await store.SaveAsync(new Label { Name = \"Production\" }, cancellationToken);","handlingStrategy":"validation","validationCode":"var existing = await store.ListAsync(cancellationToken);\nbool duplicate = existing.Any(l => l.TenantId == label.TenantId && l.Id != label.Id && l.NormalizedName == label.NormalizedName);\nif (duplicate) throw new InvalidOperationException($\"Label '{label.NormalizedName}' already exists\");","typeGuard":"bool IsNameAvailable(IEnumerable<Label> labels, Label candidate) => !labels.Any(l => l.TenantId == candidate.TenantId && l.Id != candidate.Id && l.NormalizedName == candidate.NormalizedName);","tryCatchPattern":"try\n{\n    await store.SaveAsync(label, cancellationToken);\n}\ncatch (DuplicateNormalizedNameException ex)\n{\n    logger.LogWarning(\"Label name '{Name}' already in use in tenant '{Tenant}'.\", ex.Record.NormalizedName, ex.Record.TenantId);\n    // surface a 409-style conflict to the caller\n}","preventionTips":["Normalize and de-duplicate label names before saving","Check for existing labels via the labels API before creating","De-duplicate records by NormalizedName within SaveManyAsync batches","Ensure TenantId is consistently populated in multi-tenant setups"],"tags":["labels","in-memory-store","duplicate","uniqueness"],"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-15T23:17:13.987Z"}