{"record":{"id":"cb7cc3a4a8ca596c","repo":"OrchardCMS/OrchardCore","slug":"the-type-type-can-only-be-assigned-to-a-single-feature-make","errorCode":null,"errorMessage":"The type {type} can only be assigned to a single feature. Make sure the type is not added to DI by multiple startup classes.","messagePattern":"The type (.+?) can only be assigned to a single feature\\. Make sure the type is not added to DI by multiple startup classes\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore/Extensions/Features/TypeFeatureProvider.cs","lineNumber":53,"sourceCode":"        {\n            return features;\n        }\n\n        throw new InvalidOperationException($\"Could not resolve features for type {dependency.Name}.\");\n    }\n\n    public IEnumerable<Type> GetTypesForFeature(IFeatureInfo feature)\n    {\n        return _features.Where(kv => kv.Value.Contains(feature)).Select(kv => kv.Key);\n    }\n\n    public void TryAdd(Type type, IFeatureInfo feature)\n    {\n        var features = _features.AddOrUpdate(type, (key, value) => [value], (key, features, value) => features.Contains(value) ? features : features.Append(value).ToArray(), feature);\n\n        if (features.Count() > 1 && (FeatureTypeDiscoveryAttribute.GetFeatureTypeDiscoveryForType(type)?.SingleFeatureOnly ?? false))\n        {\n            throw new InvalidOperationException($\"The type {type} can only be assigned to a single feature. Make sure the type is not added to DI by multiple startup classes.\");\n        }\n    }\n}\n","sourceCodeStart":35,"sourceCodeEnd":57,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore/Extensions/Features/TypeFeatureProvider.cs#L35-L57","documentation":"Types marked with FeatureTypeDiscovery(SingleFeatureOnly = true) must be owned by exactly one feature. TryAdd detects that such a type is being associated with a second feature and throws InvalidOperationException, because multiple startup classes are registering the same type into DI under different features.","triggerScenarios":"Calling TryAdd(type, feature) where type has the [FeatureTypeDiscovery(SingleFeatureOnly = true)] attribute and is already mapped to a different feature — typically two Startup classes in different modules exposing the same service type.","commonSituations":"Two modules each define a startup class contributing the same singleton service type; a class was moved between modules but the old registration still exists; an inherited StartupBase gets picked up by discovery in two features.","solutions":["Find the second startup class registering the type (exception names the type) and remove or disable its registration.","Ensure the class is declared in only one module/feature, removing duplicate copies after refactors.","If shared ownership is intended, drop the SingleFeatureOnly attribute requirement or use a shared abstraction package instead.","Check base classes of the startup — discovery may pick up both the base and derived types."],"exampleFix":"// before\n// ModuleA/Startup.cs and ModuleB/Startup.cs both do\nservices.AddScoped<IEmailService, EmailService>();\n\n// after\n// Keep the registration in ModuleA only; ModuleB consumes IEmailService via constructor injection.","handlingStrategy":"validation","validationCode":"// Before adding a new startup registration for a SingleFeatureOnly type:\nvar existing = featureMap.Where(kv => kv.Key == typeof(MyService)).ToList();\nif (existing.Count > 0) throw new InvalidOperationException($\"{typeof(MyService)} is already registered by {existing[0].Value.Id}.\");","typeGuard":null,"tryCatchPattern":"try { typeFeatureProvider.TryAdd(type, feature); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"single feature\"))\n{\n    _logger.LogError(ex, \"Duplicate registration of {Type} across features\", type);\n}","preventionTips":["Never implement the same service in two modules' Startup classes.","After moving a class between modules, delete the original registration.","Watch for discovery picking up both a StartupBase and its derived class.","Run an integration test that composes all modules to catch duplicates at build/CI time."],"tags":["dependency-injection","features","duplicate-registration"],"backgroundTag":"conflicting-config-options","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}