abpframework/abp · error · ArgumentException

{parameterName} (type of {type.AssemblyQualifiedName}) shoul

Error message

{parameterName} (type of {type.AssemblyQualifiedName}) should be assignable to the {typeof(TBaseType).GetFullNameWithAssemblyName()}!

What it means

Thrown by Check.AssignableTo<TBaseType>(Type, string) when the supplied type is not assignable to TBaseType (i.e. it neither is, nor implements/derives from, the expected base type). The message includes both the offending type's AssemblyQualifiedName and the expected type's full name so mismatches are obvious.

Source

Thrown at framework/src/Volo.Abp.Core/Volo/Abp/Check.cs:135

    {
        if (value == null || value.Count <= 0)
        {
            throw new ArgumentException(parameterName + " can not be null or empty!", parameterName);
        }

        return value;
    }

    [ContractAnnotation("type:null => halt")]
    public static Type AssignableTo<TBaseType>(
        Type type,
        [InvokerParameterName][NotNull] string parameterName)
    {
        NotNull(type, parameterName);

        if (!type.IsAssignableTo<TBaseType>())
        {
            throw new ArgumentException($"{parameterName} (type of {type.AssemblyQualifiedName}) should be assignable to the {typeof(TBaseType).GetFullNameWithAssemblyName()}!");
        }

        return type;
    }

    public static string? Length(
        string? value,
        [InvokerParameterName][NotNull] string parameterName,
        int maxLength,
        int minLength = 0)
    {
        if (minLength > 0)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentException(parameterName + " can not be null or empty!", parameterName);
            }

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Compare the two type names in the exception message: make the offending type implement or derive from the expected base type.
  2. Verify the assembly actually loaded is the one you compiled against (check the AssemblyQualifiedName, not just the FullName) to catch version/redirect mismatches.
  3. If multiple candidates are possible, use typeof(...) explicitly as the generic argument rather than relying on inference.
  4. Add a unit test asserting type.IsAssignableTo<TBaseType>() to catch regressions after refactors.

Example fix

// before
public class MyHandler { } // missing interface
Check.AssignableTo<IEventHandler>(typeof(MyHandler), nameof(handlerType));

// after
public class MyHandler : IEventHandler { }
Check.AssignableTo<IEventHandler>(typeof(MyHandler), nameof(handlerType));
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify assignability before calling Check.AssignableTo
if (!typeof(MyHandler).IsAssignableTo<IEventHandler>())
    throw new InvalidOperationException($"{typeof(MyHandler)} is not an IEventHandler");

Type guard

static bool IsA<TBase>(Type t) => t.IsAssignableTo<TBase>();
// usage: if (IsA<IEventHandler>(candidateType)) { ... }

Prevention

When it happens

Trigger: Calling Check.AssignableTo<IMyService>(concreteType, nameof(concreteType)) where concreteType does not implement IMyService. Common in ABP module/contributor code that reflects over assemblies or plugin types, and in typed registration helpers that must verify a candidate type fits a contract.

Common situations: Registering a plugin/convention type that was refactored and lost an interface; passing a base type where a derived type is required; version skew where an assembly was swapped and a type no longer implements the expected interface; wrong generic argument inferred by the compiler.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/4ea2bf11bded2008. Report an issue: GitHub.