OrchardCMS/OrchardCore · error · InvalidOperationException
The type can only be assigned to a single feature. Make…
Error message
The type {type} can only be assigned to a single feature. Make sure the type is not added to DI by multiple startup classes. What it means
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.
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.
Example fix
// before // ModuleA/Startup.cs and ModuleB/Startup.cs both do services.AddScoped<IEmailService, EmailService>(); // after // Keep the registration in ModuleA only; ModuleB consumes IEmailService via constructor injection.
Defensive patterns
Strategy: validation
Validate before calling
// Before adding a new startup registration for a SingleFeatureOnly type:
var existing = featureMap.Where(kv => kv.Key == typeof(MyService)).ToList();
if (existing.Count > 0) throw new InvalidOperationException($"{typeof(MyService)} is already registered by {existing[0].Value.Id}."); Try / catch
try { typeFeatureProvider.TryAdd(type, feature); }
catch (InvalidOperationException ex) when (ex.Message.Contains("single feature"))
{
_logger.LogError(ex, "Duplicate registration of {Type} across features", type);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Could not resolve extension for type
- Could not resolve main feature for type
- Could not resolve features for type
- A feature is missing a mandatory 'Id' property in the Module
- The 'Default' feature is not registered.
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/cb7cc3a4a8ca596c.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore/Extensions/Features/TypeFeatureProvider.cs:53
{
return features;
}
throw new InvalidOperationException($"Could not resolve features for type {dependency.Name}.");
}
public IEnumerable<Type> GetTypesForFeature(IFeatureInfo feature)
{
return _features.Where(kv => kv.Value.Contains(feature)).Select(kv => kv.Key);
}
public void TryAdd(Type type, IFeatureInfo feature)
{
var features = _features.AddOrUpdate(type, (key, value) => [value], (key, features, value) => features.Contains(value) ? features : features.Append(value).ToArray(), feature);
if (features.Count() > 1 && (FeatureTypeDiscoveryAttribute.GetFeatureTypeDiscoveryForType(type)?.SingleFeatureOnly ?? false))
{
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.");
}
}
}
View on GitHub (pinned to 4306c0717f)