louthy/language-ext · error · InvalidOperationException
InvalidOperationException
Error message
InvalidOperationException
What it means
EqResolver.EqResolve reflects over a type's trait implementation to find static Equals and GetHashCode methods, then wraps them in delegates using MethodInfo.Invoke. The delegate throws InvalidOperationException when the reflection Invoke returns null (the ?? branch), meaning the resolved method returned null where a bool was required — i.e. the resolved method does not have the expected signature/return semantics.
Solutions
- Fix the Eq<A> implementation so Equals(A,A) is a static method returning a non-null bool.
- Implement the Eq trait properly (or omit it so the resolver falls back to MakeDefault/EqualityComparer<A>.Default).
- Clear any cached resolver state and retry after fixing the implementation.
- Check the method signature with reflection yourself: static, two A parameters, bool return.
Example fix
// before public static object? Equals(A x, A y) => null; // wrong signature // after public static bool Equals(A x, A y) => x.Equals(y);
Defensive patterns
Strategy: try-catch
Validate before calling
var m = typeof(EqImpl).GetMethod("Equals", new[] { typeof(A), typeof(A) });
bool ok = m is { IsStatic: true } && m.ReturnType == typeof(bool); Type guard
bool IsValidEq<A>(MethodInfo? m) => m is { IsStatic: true } && m.ReturnType == typeof(bool) && m.GetParameters().Length == 2; Try / catch
try { var eq = Eq<A>.Default; var r = eq.Equals(x, y); }
catch (InvalidOperationException ex) { /* fall back to EqualityComparer<A>.Default */ } Prevention
- Write Eq<A> Equals as a static method returning plain bool
- Add tests invoking the trait equality before relying on it
- Avoid nullable or object return types on trait members
- Keep LanguageExt versions aligned across projects
When it happens
Trigger: A type's Eq<A> implementation exposes a static 'Equals(A,A)' whose invocation returns null (wrong return type, e.g. bool? or object returning null) so `(bool?)EqualsMethod.Invoke(...) ?? throw` fires when the delegate is first called.
Common situations: Hand-written Eq instances with subtly wrong signatures (non-static, wrong arity, boxed returns); refactoring changed the method shape while the resolver cached stale reflection metadata; AOT/trimming stripping or altering members.
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
- InvalidOperationException
- InvalidOperationException
- Ord attribute should have a struct type that derives from…
- Don't use Equals - use either RecordType
- Don't use Equals - use either RecordType
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/260b223236b56fd4.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/Traits/Resolve/EqResolver.cs:52
{
ResolutionError = $"Trait implementation not found for: {typeof(A).Name}";
MakeDefault();
return;
}
// Equals
var m = Resolver.Method(impl, "Equals", source, source);
if (m is null)
{
ResolutionError = $"`Equals` method not found for: {typeof(A).Name}";
MakeDefault();
return;
}
EqualsMethod = m;
EqualsMethodPtr = m.MethodHandle.GetFunctionPointer();
EqualsFunc = (x, y) => (bool?)EqualsMethod.Invoke(null, [x, y]) ?? throw new InvalidOperationException();
// GetHashCode
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()
{View on GitHub (pinned to 2f0e362824)