louthy/language-ext · error · Exception

Bug in MonadLaw or Monad.unsafeRecur. Contact language-ext…

Error message

Bug in MonadLaw or Monad.unsafeRecur. Contact language-ext maintainer via the repo.

What it means

Monad.Laws.recurIsSameAsBind validates that Monad.recur and Monad.unsafeRecur produce identical results; if they differ, this internal invariant is broken and the library throws asking the user to report it to the language-ext maintainer. It indicates a bug in the monad's Recur/unsafeRecur trait implementation, not user data.

Solutions

  1. Review and fix the F.Recur/unsafeRecur implementation so unsafeRecur matches recur's observable result
  2. Report the bug with a repro to the language-ext GitHub repo as the message instructs
  3. Bypass unsafeRecur in user code and use recur only until the bug is fixed

Example fix

// before
static K<F, S, A> unsafeRecur<S, A>((S, A) state, Func<(S, A), K<F, (S, A)>> f) => bind(f(state), s => s.Item1 < 0 ? pure(s) : unsafeRecur(s, f)); // diverges from recur semantics
// after
// implement unsafeRecur exactly as recur but without the safety check, preserving result equality: same bind recursion, no early exit
Defensive patterns

Strategy: try-catch

Validate before calling

// run the law validation in tests: MonadLaws.validate(monadInstance, examples) before shipping

Try / catch

try { laws.Validate(); }
catch (Exception ex) when (ex.Message.StartsWith("Bug in MonadLaw")) { reportToMaintainer(ex); }

Prevention

When it happens

Trigger: Running the Monad law-validation suite (via validate) on a monad F whose Recur/unsafeRecur trait methods are implemented inconsistently, so recur(...) != unsafeRecur(...).

Common situations: Writing a custom monad trait implementation with a faulty unsafeRecur (e.g. skipping the recur-loop protection or short-circuiting differently), then running the built-in law checks.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Traits/Monads/Monad/Monad.Laws.cs:64

    {
        equals ??= (fa, fb) => fa.Equals(fb);
        return ApplicativeLaw<F>.validate(equals) >>>
               leftIdentityLaw(equals)            >>>
               rightIdentityLaw(equals)           >>>
               associativityLaw(equals)           >>>
               recurIsSameAsBind(equals);
    }

    public static Validation<Error, Unit> recurIsSameAsBind(Func<K<F, int>, K<F, int>, bool>? equals = null)
    {
        var example = Seq(1, 2, 3, 4, 5, 6, 7, 8, 9);

        var result1 = Monad.recur((0, example), rec);
        var result2 = Monad.unsafeRecur((0, example), rec);
        var result3 = bind(example);

        equals ??= (fa, fb) => fa.Equals(fb);
        if(!equals(result1, result2)) throw new Exception("Bug in MonadLaw or Monad.unsafeRecur. Contact language-ext maintainer via the repo.");
        if (!equals(result1, result3))
        {
            return Validation.Fail<Error, Unit>(
                Error.New($"Monad trait implementation for {typeof(F).Name}.Recur gives a different "    +
                          $"result to the equivalent recursive {typeof(F).Name}.Bind.  This suggests "   +
                          $"an implementation bug, most likely in {typeof(F).Name}.Recur, but possibly " +
                          $"in {typeof(F).Name}.Bind."));
        }

        return Validation.Success<Error, Unit>(unit);
        
        K<F, Next<(int Total, Seq<int> Values), int>> rec((int Total, Seq<int> Values) pair) =>
            pair.Values switch
            {
                []          => F.Pure(Next.Done<(int, Seq<int>), int>(pair.Total)),
                var (x, xs) => F.Pure(Next.Loop<(int, Seq<int>), int>((pair.Total + x, xs))) 
            };

View on GitHub (pinned to 2f0e362824)