louthy/language-ext · error · InvalidOperationException

InvalidOperationException

Error message

InvalidOperationException

What it means

HashableResolver.HashableResolve reflects the Hashable<A> implementation's GetHashCode and wraps it in a delegate. The delegate throws InvalidOperationException when the reflection Invoke yields null where an int was expected — the resolved member doesn't match the required static int GetHashCode(A) contract.

Solutions

  1. Correct Hashable<A>.GetHashCode to be a static method returning a non-null int.
  2. Remove the faulty implementation so HashableResolver.MakeDefault (DefaultGetHashCode) is used.
  3. Handle null A values inside the hash method instead of returning null.
  4. Confirm the resolved MethodInfo signature before use in tests.

Example fix

// before
public static int? GetHashCode(A x) => x == null ? null : x.Hash;
// after
public static int GetHashCode(A x) => x?.GetHashCode() ?? 0;
Defensive patterns

Strategy: validation

Validate before calling

var m = typeof(HashableImpl).GetMethod("GetHashCode", new[] { typeof(A) });
bool ok = m is { IsStatic: true } && m.ReturnType == typeof(int);

Type guard

bool IsValidHashable<A>(MethodInfo? m) => m is { IsStatic: true } && m.ReturnType == typeof(int) && m.GetParameters().Length == 1;

Try / catch

try { var h = Hashable<A>.Default.GetHashCode(x); }
catch (InvalidOperationException ex) { throw new InvalidOperationException("Hashable<A>.GetHashCode returned null; fix its signature to static int", ex); }

Prevention

When it happens

Trigger: First use of a Hashable<A> instance whose static GetHashCode(A) returns null (or a nullable/boxed value convertible to null) when called through the resolver's GetHashCodeFunc delegate.

Common situations: Custom Hashable implementations with wrong return types; generic type hashing delegates that return null for null arguments; code-gen or source-generation mismatches after upgrades.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Traits/Resolve/HashableResolver.cs:43

        var impl = Resolver.Find(source, "Hashable");
        if (impl is null)
        {
            ResolutionError = $"Trait implementation not found for: {typeof(A).Name}";
            MakeDefault();
            return;
        }
        
        var m = Resolver.Method(impl, "GetHashCode", source);
        if (m is null)
        {
            ResolutionError = $"`GetHashCode` method not found for: {typeof(A).Name}";
            MakeDefault();
            return;
        }

        GetHashCodeMethod    = m;
        GetHashCodeMethodPtr = m.MethodHandle.GetFunctionPointer();
        GetHashCodeFunc      = x => (int?)GetHashCodeMethod.Invoke(null, [x]) ?? throw new InvalidOperationException();
    }
    
    static void MakeDefault()
    {
        GetHashCodeFunc      = DefaultGetHashCode;
        GetHashCodeMethod    = GetHashCodeFunc.Method;
        GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer();
    }

    static int DefaultGetHashCode(A value) =>
        value is null ? 0 : value.GetHashCode();
}

View on GitHub (pinned to 2f0e362824)