OrchardCMS/OrchardCore · error · InvalidOperationException

Could not resolve features for type

Error message

Could not resolve features for type {dependency.Name}.

What it means

GetFeaturesForDependency returns all features associated with a DI service type. Because the provider only knows types that were registered through TryAdd during feature type discovery, an unregistered type yields no entry and this InvalidOperationException is thrown rather than returning an empty list.

Solutions

  1. Register the service from a module's Startup/StartupBase class so feature discovery maps it.
  2. Only query types resolved from the shell's module-scoped container.
  3. In tests, populate the provider with TryAdd for the types under inspection before querying.

Example fix

// before
var features = provider.GetFeaturesForDependency(typeof(ManuallyRegisteredService)); // throws

// after
provider.TryAdd(typeof(ManuallyRegisteredService), moduleFeature);
var features = provider.GetFeaturesForDependency(typeof(ManuallyRegisteredService));
Defensive patterns

Strategy: type-guard

Validate before calling

bool registered =
    ((IEnumerable<KeyValuePair<Type, IFeatureInfo[]>>)provider.GetRegisteredTypes())
        .Any(kv => kv.Key == typeof(T)); // expose or mirror the map if the API allows

Type guard

bool IsFeatureMapped(Type dependency)
{
    try { provider.GetFeaturesForDependency(dependency); return true; }
    catch (InvalidOperationException) { return false; }
}

Try / catch

try { var features = provider.GetFeaturesForDependency(type); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Could not resolve features")) { features = []; }

Prevention

When it happens

Trigger: Calling GetFeaturesForDependency(type) for a type not registered by any module startup — the ConcurrentDictionary lookup misses and the throw executes.

Common situations: Inspecting services added directly to the service collection (host or theme-level) rather than via module Startup; calling during unit tests with a partially populated TypeFeatureProvider.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/6a58a2a9e44d7c58. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore/Extensions/Features/TypeFeatureProvider.cs:39

    {
        if (_features.TryGetValue(dependency, out var features))
        {
            // Gets the first feature that has the same ID as the extension, if any. 
            // Otherwise returns the first feature.
            return features.FirstOrDefault(feature => feature.Extension.Id == feature.Id) ?? features.First();
        }

        throw new InvalidOperationException($"Could not resolve main feature for type {dependency.Name}.");
    }

    public IEnumerable<IFeatureInfo> GetFeaturesForDependency(Type dependency)
    {
        if (_features.TryGetValue(dependency, out var features))
        {
            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)