louthy/language-ext · error · Exception

Ord attribute should have a struct type that derives from…

Error message

Ord attribute should have a struct type that derives from LanguageExt.Traits.Ord<> passed as its argument

What it means

OrdAttribute.Validate is applied to the type passed to the [Ord] attribute and throws unless that type implements LanguageExt.Traits.Ord<T>. The [Ord] attribute on a Record must point at a struct that provides ordering semantics for the record; anything else makes the generated comparison invalid.

Solutions

  1. Make the type passed to [Ord] a struct that implements LanguageExt.Traits.Ord<T> for your record type T.
  2. If migrating, update the comparer to the LanguageExt.Traits.Ord<T> interface (new namespace and shape) instead of the legacy Ord<T>.
  3. Ensure the comparer is a struct, not a class, as required by the attribute contract.

Example fix

// before
[Ord(typeof(MyOrd))]  // MyOrd implements old LanguageExt.Ord<Rec>
public partial record Rec(int X);

// after
public readonly struct MyOrd : LanguageExt.Traits.Ord<Rec>
{
    public int Compare(Rec x, Rec y) => x.X.CompareTo(y.X);
}
[Ord(typeof(MyOrd))]
public partial record Rec(int X);
Defensive patterns

Strategy: validation

Validate before calling

// before applying the attribute
typeof(MyOrd).GetInterfaces().Any(i =>
    i.IsGenericType && i.GetGenericTypeDefinition() == typeof(LanguageExt.Traits.Ord<>));

Type guard

static bool IsValidOrdComparer<T>(T t) => t is LanguageExt.Traits.Ord<RecordType>;

Prevention

When it happens

Trigger: Decorating a record with [Ord(typeof(X))] where X does not implement LanguageExt.Traits.Ord<T> (or implements the old LanguageExt.Ord<T> interface), so the interface check for a name starting with "LanguageExt.Traits.Ord`1" fails.

Common situations: Migrating from LanguageExt 3.x/4.x (where Ord lived in a different namespace) to 5.x Traits namespace; pointing the attribute at a class instead of a struct; pointing it at the record itself instead of a separate comparer type.

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/daa505af210e999d. Report an issue: GitHub.

Appendix: source

Thrown at LanguageExt.Core/DataTypes/Record/Attributes.cs:41

                 .ImplementedInterfaces
                 .AsIterable()     
                 .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Eq`1")))
        {
            throw new Exception("Eq attribute should have a struct type that derives from LanguageExt.Traits.Eq<> passed as its argument");
        }
    }
}

public class OrdAttribute : Attribute
{
    public OrdAttribute(Type type)
    {
        if (!type.GetTypeInfo()
                 .ImplementedInterfaces
                 .AsIterable()
                 .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Ord`1")))
        {
            throw new Exception("Ord attribute should have a struct type that derives from LanguageExt.Traits.Ord<> passed as its argument");
        }
    }
}

public class HashableAttribute : Attribute
{
    public HashableAttribute(Type type)
    {
        if (!type.GetTypeInfo()
                 .ImplementedInterfaces
                 .AsIterable()
                 .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Hashable`1")))
        {
            throw new Exception("Hashable attribute should have a struct type that derives from LanguageExt.Traits.Hashable<> passed as its argument");
        }
    }
}

View on GitHub (pinned to 2f0e362824)