louthy/language-ext · error · IndexOutOfRangeException

IndexOutOfRangeException

Error message

IndexOutOfRangeException

What it means

LanguageExt's Seq.head lens throws IndexOutOfRangeException when the target Seq is empty. Lenses are total-style accessors but the head lens cannot produce a value for an empty sequence, so it throws instead of returning Option. The exception comes from the Get (or Set) lambda of the lens when la.IsEmpty is true.

Solutions

  1. Check la.IsEmpty (or la.Count == 0) before applying the head lens
  2. Use the headOrNone lens instead, which returns Option<A> and never throws
  3. Use Seq.head() / HeadOrNone() extension functions rather than the lens
  4. Wrap the lens application in try-catch for IndexOutOfRangeException if the empty case is expected

Example fix

// before
var first = Seq.head.Get(mySeq);
// after
var first = mySeq.IsEmpty ? Option<A>.None : Seq.headOrNone.Get(mySeq);
Defensive patterns

Strategy: validation

Validate before calling

if (seq.IsEmpty) throw new InvalidOperationException("Cannot get head of empty Seq");
var first = Seq.head.Get(seq);

Type guard

static bool HasHead<A>(Seq<A> s) => !s.IsEmpty;

Try / catch

try { first = Seq.head.Get(seq); }
catch (IndexOutOfRangeException) { first = default; /* empty seq */ }

Prevention

When it happens

Trigger: Calling Seq<A>.head.Get() on an empty Seq, or head.Set() on an empty Seq. Any code path that applies the head lens without first checking IsEmpty.

Common situations: Taking the first element of a query/filter result that returned no items; processing a file or stream that produced zero lines; refactoring from List.First() to Seq.head without preserving the empty-collection check.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Immutable Collections/Seq/Seq.cs:99

            : Tail.IsEmpty 
                ? Head.Value
                : (Head.Value, Tail);

    public void Deconstruct(out A head, out Seq<A> tail)
    {
        head = Head.IfNone(() => throw Exceptions.SequenceEmpty);
        tail = Tail;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ReadOnlySpan<A> AsSpan() =>
        Value.AsSpan();

    /// <summary>
    /// Head lens
    /// </summary>
    public static Lens<Seq<A>, A> head => Lens<Seq<A>, A>.New(
        Get: la => la.IsEmpty ? throw new IndexOutOfRangeException() : la[0],
        Set: a => la => la.IsEmpty ? throw new IndexOutOfRangeException() : a.Cons(la.Tail)
    );

    /// <summary>
    /// Head or none lens
    /// </summary>
    public static Lens<Seq<A>, Option<A>> headOrNone => Lens<Seq<A>, Option<A>>.New(
        Get: la => la.Head,
        Set: a => la => la.IsEmpty || a.IsNone ? la : a.Value.Cons(la.Tail!)!
    );

    /// <summary>
    /// Tail lens
    /// </summary>
    public static Lens<Seq<A>, Seq<A>> tail => Lens<Seq<A>, Seq<A>>.New(
        Get: la => la.IsEmpty ? Empty : la.Tail,
        Set: a => la => la.IsEmpty ? a : ((A)la.Head).Cons(a)
    );

View on GitHub (pinned to 2f0e362824)