louthy/language-ext · error · InvalidOperationException
InvalidOperationException
Error message
InvalidOperationException
What it means
OrdResolver.MakeComparer resolves the Ord<A> implementation's Compare method via reflection and wraps it in CompareFunc. That delegate throws InvalidOperationException if the Invoke result is null where an int was required, meaning the resolved Compare member doesn't conform to static int Compare(A,A).
Solutions
- Fix Compare to be static, take (A, A), and return a non-null int.
- Remove the broken Ord implementation so OrdResolver.MakeDefault (Comparer<A>.Default) applies.
- Add a unit test that exercises comparison through the trait before shipping.
- Audit all static members named 'Compare' on the type for accidental overload collisions.
Example fix
// before public static int? Compare(A x, A y) => null; // after public static int Compare(A x, A y) => Comparer<A>.Default.Compare(x, y);
Defensive patterns
Strategy: validation
Validate before calling
var m = typeof(OrdImpl).GetMethod("Compare", new[] { typeof(A), typeof(A) });
bool ok = m is { IsStatic: true } && m.ReturnType == typeof(int); Type guard
bool IsValidCompare<A>(MethodInfo? m) => m is { IsStatic: true } && m.ReturnType == typeof(int) && m.GetParameters().Length == 2; Try / catch
try { ordered = items.OrderBy(x => x, Ord<A>.Default); }
catch (InvalidOperationException) { ordered = items.OrderBy(x => x, Comparer<A>.Default); } Prevention
- Static int Compare(A, A) — no nullable returns
- Smoke-test sorting a sample collection early in development
- Avoid overloads named Compare with different shapes on the same type
- Fallback to Comparer<A>.Default in infrastructure code
When it happens
Trigger: Using Ord<A>-based ordering (sort, min/max, comparison APIs) on a type whose Ord implementation's Compare(A,A) returns null (wrong return type or null-producing body) when the delegate is invoked.
Common situations: Hand-rolled Ord instances with nullable int returns; refactors that changed Compare's signature; reflection-based resolution picking a wrong overload; trimming removing the real method.
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…
- InvalidOperationException
- Hashable attribute should have a struct type that derives…
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/8b9ffd61d40431e3.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/Traits/Resolve/OrdResolver.cs:77
{
ResolutionError = $"Trait implementation not found for: {source.Name}";
MakeDefault();
return;
}
// Compare
var m = Resolver.Method(impl, "Compare", source, source);
if (m is null)
{
ResolutionError = $"`Compare` method not found for: {source.Name}";
MakeDefault();
return;
}
CompareMethod = m;
CompareMethodPtr = m.MethodHandle.GetFunctionPointer();
CompareFunc = (x, y) => (int?)CompareMethod.Invoke(null, [x, y]) ?? throw new InvalidOperationException();
// Equals
m = Resolver.Method(impl, "Equals", source, source);
if (m is null)
{
ResolutionError = $"`Equals` method not found for: {source.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);View on GitHub (pinned to 2f0e362824)