{"record":{"id":"c997088652ee1a84","repo":"elsa-workflows/elsa-core","slug":"output-converter-id-id-is-already-registered-or-differs-from","errorCode":null,"errorMessage":"Output converter ID '{id}' is already registered or differs from '{existingRegistration.Descriptor.Id}' only by case.","messagePattern":"Output converter ID '(.+?)' is already registered or differs from '(.+?)' only by case\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Core/Extensions/OutputConverterServiceCollectionExtensions.cs","lineNumber":51,"sourceCode":"            typeof(TConverter),\n            serviceLifetime));\n        services.TryAddSingleton<IOutputConverterRegistry, OutputConverterRegistry>();\n        return services;\n    }\n\n    private static void ValidateUniqueId(IServiceCollection services, string id)\n    {\n        if (string.IsNullOrWhiteSpace(id))\n            throw new ArgumentException(\"Output converter IDs cannot be empty.\", nameof(id));\n\n        var existingRegistration = services\n            .Where(x => x.ServiceType == typeof(OutputConverterRegistration))\n            .Select(x => x.ImplementationInstance)\n            .OfType<OutputConverterRegistration>()\n            .FirstOrDefault(x => string.Equals(x.Descriptor.Id, id, StringComparison.OrdinalIgnoreCase));\n\n        if (existingRegistration != null)\n            throw new InvalidOperationException($\"Output converter ID '{id}' is already registered or differs from '{existingRegistration.Descriptor.Id}' only by case.\");\n    }\n}\n","sourceCodeStart":33,"sourceCodeEnd":54,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Core/Extensions/OutputConverterServiceCollectionExtensions.cs#L33-L54","documentation":"ValidateUniqueId checks existing OutputConverterRegistration entries case-insensitively and throws InvalidOperationException when the ID is already registered or differs only by case. This prevents ambiguous converter resolution. Registration happens via AddOutputConverter on the IServiceCollection.","triggerScenarios":"Registering two output converters whose IDs collide (e.g. \"xml\" vs \"XML\"); a library already registers a converter with the same ID; re-running registration code twice in the same service collection.","commonSituations":"Two modules each registering a converter for the same media type with the same ID; duplicate feature auto-registration; case inconsistency between registrations.","solutions":["Choose a unique ID for the new converter","Reuse the existing converter instead of registering a duplicate","Match the exact casing of the existing registration if intentional replacement is desired (and remove the old registration first)","Check registered OutputConverterRegistration entries in the DI container before adding"],"exampleFix":"// before\nservices.AddOutputConverter(new OutputConverterDescriptor { Id = \"XML\" });\n// after\nservices.AddOutputConverter(new OutputConverterDescriptor { Id = \"application/xml\" });","handlingStrategy":"validation","validationCode":"var taken = services.Where(x => x.ServiceType == typeof(OutputConverterRegistration)).OfType<IServiceProviderIsService>().Any(); // or inspect registrations for a matching Descriptor.Id (OrdinalIgnoreCase) before adding","typeGuard":"static bool IsDuplicateId(IServiceCollection s, string id) => s.Where(x => x.ServiceType == typeof(OutputConverterRegistration)).Select(x => x.ImplementationInstance).OfType<OutputConverterRegistration>().Any(r => string.Equals(r.Descriptor.Id, id, StringComparison.OrdinalIgnoreCase));","tryCatchPattern":"try { services.AddOutputConverter(descriptor); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"Output converter ID\")) { logger.LogWarning(ex, \"Duplicate output converter id skipped\"); }","preventionTips":["Use stable, descriptive IDs (media types) and keep a registry of them","Avoid case-variant duplicates by normalizing IDs to lowercase before registering"],"tags":["dependency-injection","duplicate-registration"],"backgroundTag":"conflicting-config-options","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"}