{"record":{"id":"609ecaca094fa13f","repo":"elsa-workflows/elsa-core","slug":"duplicate-ingress-source-registration-for-name-source-name","errorCode":null,"errorMessage":"Duplicate ingress source registration for name '{source.Name}'.","messagePattern":"Duplicate ingress source registration for name '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Runtime/Services/IngressSourceRegistry.cs","lineNumber":55,"sourceCode":"            return _entries.Values.Select(e => e.Source).ToArray();\n        }\n    }\n\n    private void EnsureMaterialized()\n    {\n        // Double-checked lock: a `_entries.Count > 0` guard is not atomic with the population loop, so concurrent\n        // first callers could both observe an empty dictionary, both iterate _sourcesFactory.Value, and both call\n        // TryAdd — losing the duplicate-name guarantee and crashing the second caller's execution cycle start with\n        // \"Duplicate ingress source registration\". A short lock around the one-time population is sufficient;\n        // steady-state reads short-circuit on the volatile flag without taking the lock.\n        if (_materialized) return;\n        lock (_materializeSync)\n        {\n            if (_materialized) return;\n            foreach (var source in _sourcesFactory.Value)\n            {\n                if (!_entries.TryAdd(source.Name, new Entry(source)))\n                    throw new InvalidOperationException($\"Duplicate ingress source registration for name '{source.Name}'.\");\n            }\n            _materialized = true;\n        }\n    }\n\n    /// <inheritdoc />\n    public IReadOnlyCollection<IngressSourceSnapshot> Snapshot()\n    {\n        EnsureMaterialized();\n        return _entries.Values\n            .Select(e => new IngressSourceSnapshot(e.Source.Name, e.State, e.LastError, e.LastTransitionAt))\n            .ToArray();\n    }\n\n    /// <inheritdoc />\n    public ValueTask MarkPauseFailedAsync(string name, string reason, Exception? error = null)\n    {\n        EnsureMaterialized();","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Runtime/Services/IngressSourceRegistry.cs#L37-L73","documentation":"IngressSourceRegistry.EnsureMaterialized lazily builds entries from the configured sources factory and requires each source to have a unique name. When two or more registered ingress sources share the same name, TryAdd fails and InvalidOperationException is thrown. This is a configuration invariant protecting name-based lookup of ingress sources.","triggerScenarios":"Registering two or more ingress sources with the same Name via the diagnostics console-logs ingress source registration, then first accessing Sources, Snapshot, MarkPauseFailedAsync, RecordTransition, or GetState, which triggers materialization.","commonSituations":"Two features both registering a source with a default name (e.g. 'console'); copying registration code without renaming the source; duplicate AddX calls in DI configuration.","solutions":["Find the duplicate registration and give each ingress source a distinct Name.","Guard registration code so the same source is not added twice (e.g. check if already registered before adding).","Review DI setup for duplicate AddIngressSource-style calls across feature modules.","Add a startup-time uniqueness check for source names to fail fast with a clearer message."],"exampleFix":"// before\noptions.AddSource(new ConsoleIngressSource { Name = \"console\" });\noptions.AddSource(new FileIngressSource { Name = \"console\" }); // duplicate\n\n// after\noptions.AddSource(new ConsoleIngressSource { Name = \"console\" });\noptions.AddSource(new FileIngressSource { Name = \"console-file\" });","handlingStrategy":"validation","validationCode":"var names = options.Sources.Select(s => s.Name).ToList();\nvar duplicates = names.GroupBy(n => n).Where(g => g.Count() > 1).ToList();\nif (duplicates.Any()) throw new InvalidOperationException($\"Duplicate ingress source names: {string.Join(\", \", duplicates.Select(d => d.Key))}\");","typeGuard":null,"tryCatchPattern":"try\n{\n    _ = registry.Sources;\n}\ncatch (InvalidOperationException ex) when (ex.Message.StartsWith(\"Duplicate ingress source registration\"))\n{\n    logger.LogCritical(ex, \"Ingress source names must be unique.\");\n}","preventionTips":["Give every ingress source a unique, explicit Name","Guard against duplicate Add calls in DI setup","Validate source name uniqueness at startup"],"tags":["configuration","duplicate-registration","ingress","runtime"],"backgroundTag":"invalid-config-value","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"}