dotnet runtime_exception ai_generated true

System.InvalidCastException: Unable to cast object of type 'X' to type 'Y'.

ID: dotnet/invalid-cast-exception

Also available as: JSON · Markdown
89%Fix Rate
90%Confidence
80Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
8 active

Root Cause

InvalidCastException occurs when an explicit cast or unboxing operation fails at runtime because the actual object type is not compatible with the target type. Common scenarios include: casting objects from untyped collections (ArrayList, DataRow fields), incorrect unboxing of value types (e.g., casting an int boxed as object to long), COM interop type mismatches, and interface cast failures when the same assembly is loaded from different paths. On .NET 8 Linux, a particularly common cause is assembly loading contexts where the 'same' type loaded from different AssemblyLoadContexts are treated as distinct types. Fix by using safe casting patterns (as/is), proper generic collections, and ensuring consistent assembly loading.

generic

Workarounds

  1. 92% success Use pattern matching or the 'as' operator with null checks instead of direct casts
    Replace direct casts with safe patterns: instead of var result = (MyType)obj; use if (obj is MyType result) { /* use result */ } else { /* handle mismatch, log obj.GetType().AssemblyQualifiedName */ }. For value type unboxing, ensure the exact type matches: an int boxed as object must be unboxed to int first, then converted: var value = (long)(int)obj; not (long)obj. Use pattern matching with type checks for polymorphic scenarios
  2. 90% success Replace untyped collections with generic equivalents to get compile-time type safety
    Replace ArrayList with List<T>, Hashtable with Dictionary<TKey, TValue>, and DataRow field access with strongly-typed models. For DataRow: instead of (decimal)row["Price"], use row.Field<decimal>("Price") which handles DBNull and performs proper type conversion. For legacy APIs returning object, create typed wrapper methods: public static T GetValue<T>(DataRow row, string column) => row.IsNull(column) ? default! : row.Field<T>(column);
  3. 85% success Fix assembly loading context issues by ensuring a single copy of shared types
    When the error shows the same type name for source and target (e.g., 'Unable to cast MyApp.Models.User to MyApp.Models.User'), the type is loaded from different assembly contexts. Diagnose with: Console.WriteLine(obj.GetType().Assembly.Location); Console.WriteLine(typeof(MyType).Assembly.Location); Fix by ensuring plugins or dynamically loaded assemblies share the host's types via AssemblyLoadContext.Default or by using a shared contract assembly. In plugin scenarios, configure the AssemblyDependencyResolver to resolve shared types from the host context

Dead Ends

Common approaches that don't work:

  1. Suppressing the exception by wrapping every cast in try-catch and returning a default value 90% fail

    This masks the real type mismatch and leads to silent data corruption or logic errors downstream. The cast failure indicates a fundamental type contract violation that needs to be understood and fixed, not hidden. Default values propagating through the system create bugs that are much harder to diagnose later

  2. Using Convert.ChangeType() as a universal replacement for direct casts 75% fail

    Convert.ChangeType() only works for types that implement IConvertible (primitive types, string, DateTime). It will throw InvalidCastException or FormatException for custom types, complex objects, and many framework types. It also does not handle nullable types correctly, throwing for Nullable<T> targets

  3. Assuming the cast fails because of an inheritance issue and adding a base class or interface 85% fail

    When the types look identical but the cast fails (common error message: 'Unable to cast Type to Type' with same names), the issue is usually assembly loading: the same type loaded from two different assembly contexts are distinct CLR types. Adding inheritance relationships does not help because the runtime sees them as completely unrelated types from different assemblies

Error Chain

Leads to:
Preceded by:
Frequently confused with: