louthy/language-ext · error · NotSupportedException
must be a Monoid
Error message
{typeof(FAIL).Name} must be a Monoid What it means
The Alternative instance's Empty for Validation<FAIL> requires FAIL to be a Monoid so it can produce an empty failure value. When MonoidInstance<FAIL> resolves to none, the trait implementation throws NotSupportedException stating the FAIL type must be a Monoid.
Solutions
- Provide a Monoid instance for FAIL (implement Monoid<FAIL> with Empty and Append, or derive via traits/deriving)
- Use a FAIL type that already has a Monoid (e.g. Seq<string> / Error types with mempty)
- Avoid Alternative.Empty for Validation — construct failures with Validation.Fail directly
- Register the instance in the trait-collection initialization so MonoidInstance<FAIL>.Instance is Some
Example fix
// before
var e = empty<Validation<Fail>, int>(); // NotSupportedException: Fail must be a Monoid
// after
public struct FailMonoid : Monoid<Fail>
{
public Fail Empty => Fail.Empty;
public Fail Append(Fail x, Fail y) => x.Combine(y);
}
// then register/derive so MonoidInstance<Fail> resolves, and call empty<Validation<Fail>, int>() Defensive patterns
Strategy: validation
Validate before calling
var ok = MonoidInstance<FAIL>.Instance is { IsSome: true };
if (!ok) throw new InvalidOperationException("FAIL must have a Monoid before Alternative.Empty"); Type guard
bool HasMonoid<FAIL>() => MonoidInstance<FAIL>.Instance.IsSome;
Try / catch
try { var e = empty<Validation<FAIL>, A>(); }
catch (NotSupportedException ex) when (ex.Message.EndsWith("must be a Monoid")) { var e = Validation.Fail<FAIL, A>(default!); } Prevention
- Always give FAIL types a Monoid instance
- Use error types with existing Monoids (Seq<string>, etc.)
- Check instance resolution in generic helper code
- Document Monoid requirements on custom error types
When it happens
Trigger: Invoking the Alternative.Empty<A>() member for K<Validation<FAIL>, A> (e.g. via empty()/Prelude.empty or generic Alternative combinators) where FAIL has no registered Monoid instance.
Common situations: Using Validation with a custom FAIL type (e.g. string, custom error record) that lacks a Monoid derivation; calling generic code that requires Alternative.Empty on Validation; upgrading LanguageExt where instance resolution became stricter.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Type is not a valid monoid
- Type is not a valid monoid
- Ord attribute should have a struct type that derives from…
- Hashable attribute should have a struct type that derives…
- Don't use Equals - use either RecordType
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/e8c907f943a3651c.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/Monads/Alternative Monads/Validation/Trait/Validation.TraitImpl.cs:68
Validation.SuccessI<FAIL, A>(value);
static K<Validation<FAIL>, B> Applicative<Validation<FAIL>>.Apply<A, B>(
K<Validation<FAIL>, Func<A, B>> mf,
K<Validation<FAIL>, A> ma) =>
mf.ApplyI(ma, SemigroupInstance<FAIL>.Instance);
static K<Validation<FAIL>, B> Applicative<Validation<FAIL>>.Apply<A, B>(
K<Validation<FAIL>, Func<A, B>> mf,
Memo<Validation<FAIL>, A> ma) =>
mf.ApplyI(ma.Value, SemigroupInstance<FAIL>.Instance);
static K<Validation<FAIL>, A> Alternative<Validation<FAIL>>.Empty<A>() =>
MonoidInstance<FAIL>.Instance switch
{
{ IsSome: true, Value: { } monoid } =>
Validation.FailI<FAIL, A>(monoid.Empty),
_ => throw new NotSupportedException($"{typeof(FAIL).Name} must be a Monoid")
};
static K<Validation<FAIL>, A> MonoidK<Validation<FAIL>>.Empty<A>() =>
MonoidInstance<FAIL>.Instance switch
{
{ IsSome: true, Value: { } monoid } =>
Validation.FailI<FAIL, A>(monoid.Empty),
_ => throw new NotSupportedException($"{typeof(FAIL).Name} must be a Monoid")
};
static K<Validation<FAIL>, A> SemigroupK<Validation<FAIL>>.Combine<A>(
K<Validation<FAIL>, A> ma,
K<Validation<FAIL>, A> mb) =>
ma.As().CombineFirst(mb.As(), SemigroupInstance<FAIL>.Instance);
static K<Validation<FAIL>, A> Choice<Validation<FAIL>>.Choose<A>(
K<Validation<FAIL>, A> ma,View on GitHub (pinned to 2f0e362824)