louthy/language-ext · error · InvalidOperationException

InvalidOperationException

Error message

InvalidOperationException

What it means

Resolver.GetHashCodeMethodAlways searches HashableResolve/EqResolve/OrdResolve for a usable GetHashCode MethodInfo and, when none of the resolvers report Exists, falls back to HashableResolve<T>.GetMethod("GetHashCode") and throws InvalidOperationException if that is also absent. It means the requested type has no Hashable/Eq/Ord instance registered, so a hash-code method is guaranteed unavailable.

Solutions

  1. Implement a Hashable<T> instance (or Eq<T>/Ord<T>) for the type in the same or a referenced assembly.
  2. Name the conventional resolver class correctly so Resolver.Find can locate it (prefix 'Hashable' + type name).
  3. Verify the assembly containing the instance is referenced and loadable.
  4. Check the nullable Resolver.GetHashCodeMethod first if your code can fall back to BCL GetHashCode instead.

Example fix

// before
var set = HashSet.create<Money>(...); // InvalidOperationException: no hashable instance
// after
public class MoneyHashable : Hashable<Money>
{
    public override int GetHashCode(Money x) => x.Amount.GetHashCode() ^ x.Currency.GetHashCode();
    public override bool Equals(Money x, Money y) => x.Amount == y.Amount && x.Currency == y.Currency;
}
Defensive patterns

Strategy: fallback

Validate before calling

// pre-check before hashing API that uses 'Always' resolution
if (!HashableResolve<Money>.Exists && !EqResolve<Money>.Exists && !OrdResolve<Money>.Exists)
    throw new InvalidOperationException($"No Hashable/Eq/Ord instance for Money");

Type guard

static bool HasHashableInstance<T>() => HashableResolve<T>.Exists || EqResolve<T>.Exists || OrdResolve<T>.Exists;

Try / catch

MethodInfo hashMethod;
try { hashMethod = Resolver.GetHashCodeMethodAlways(typeof(T)); }
catch (InvalidOperationException)
{
    hashMethod = null; // fall back to EqualityComparer<T>.Default.GetHashCode
}

Prevention

When it happens

Trigger: Calling APIs that require hashing (dictionaries, hash sets, Hashablerelude helpers) for a type T where HashableResolve<T>, EqResolve<T> and OrdResolve<T>.Exists are all false — i.e. no static Hashable<T>/Eq<T>/Ord<T> instance exists for T and no 'Hashable<T>'-named convention class was found.

Common situations: Forgetting to write a Hashable<T>/Eq<T> instance for a custom struct/class before using it as a key; type-named convention classes misspelled (resolver searches for 'HashableT'-style names via Resolver.Find); instances defined in an assembly that failed to load.

Related errors


AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15). Data as JSON: /api/errors/083cb4628acfa83a. Report an issue: GitHub.

Appendix: source

Thrown at LanguageExt.Core/Traits/Resolve/Resolver.cs:114

        return null;
    }

    public static MethodInfo GetHashCodeMethodAlways(Type type)
    {
        Type[] traits = [typeof(HashableResolve<>), typeof(EqResolve<>), typeof(OrdResolve<>)];
        foreach (var trait in traits)
        {
            var impl   = trait.MakeGenericType(type);
            var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false;
            if (exists)
            {
                var method = impl.GetMethod("GetHashCode", BindingFlags.Static | BindingFlags.Public, [type]);
                if (method is not null) return method;
            }
        }
        
        var impl2   = typeof(HashableResolve<>).MakeGenericType(type);
        return impl2.GetMethod("GetHashCode", BindingFlags.Static | BindingFlags.Public, [type]) ?? throw new InvalidOperationException();
    }

    public static MethodInfo? GetEqualsMethod(Type type)
    {
        Type[] traits = [typeof(EqResolve<>), typeof(OrdResolve<>)];
        foreach (var trait in traits)
        {
            var impl   = trait.MakeGenericType(type);
            var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false;
            if (exists)
            {
                var method = impl.GetMethod("Equals", BindingFlags.Static | BindingFlags.Public, [type, type]);
                if (method is not null) return method;
            }
        }
        return null;
    }

View on GitHub (pinned to 2f0e362824)