louthy/language-ext · error · InvalidCastException

InvalidCastException

Error message

InvalidCastException

What it means

Resources.Acquire throws InvalidCastException when the IDisposable value passed in boxes to null. The code casts the generic value to object and treats null as an invalid cast rather than a null-argument problem, so passing a null reference (or Nullable<T> with no value) to Acquire produces this exception instead of ArgumentNullException.

Solutions

  1. Ensure the value passed to Acquire is a non-null IDisposable before calling it.
  2. If null is a legitimate possibility, use a Match/IfNone pattern or check value is not null first.
  3. If you own the caller, throw a clearer ArgumentNullException at your boundary instead of letting this surface.

Example fix

// before
resources.Acquire(GetStream()); // GetStream() may return null
// after
var stream = GetStream() ?? throw new InvalidOperationException("stream not configured");
resources.Acquire(stream);
Defensive patterns

Strategy: type-guard

Validate before calling

if (value is null) throw new ArgumentNullException(nameof(value));

Type guard

static bool IsUsable<A>(A v) where A : IDisposable => v is not null;

Try / catch

try { resources.Acquire(stream); } catch (InvalidCastException) { /* null resource passed */ }

Prevention

When it happens

Trigger: Calling Resources.Acquire<A>(null) where A is a reference type implementing IDisposable, or Acquire on a Nullable value type whose HasValue is false (e.g. default(Nullable<FileStream>) boxed to null).

Common situations: Passing an uninitialized field or a method returning null (e.g. a config lookup for a stream/connection) into resource tracking during IO bracket/using-style composition.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Effects/IO/Resources.cs:52

        {
            item.Value.Release().Run(disposeEnv);
        }
        return default;
    }

    public Unit DisposeU()
    {
        Dispose();
        return default;
    }

    public IO<Unit> DisposeIO() =>
        IO.lift(_ => DisposeU());

    public Unit Acquire<A>(A value) where A : IDisposable
    {
        var obj = (object?)value;
        if (obj is null) throw new InvalidCastException();
        return resources.TryAdd(obj, new TrackedResourceDisposable<A>(value));
    }

    public Unit AcquireAsync<A>(A value) where A : IAsyncDisposable
    {
        var obj = (object?)value;
        if (obj is null) throw new InvalidCastException();
        return resources.TryAdd(obj, new TrackedResourceAsyncDisposable<A>(value));
    }

    public Unit Acquire<A>(A value, Func<A, IO<Unit>> release) 
    {
        var obj = (object?)value;
        if (obj is null) throw new InvalidCastException();
        return resources.TryAdd(obj, new TrackedResourceWithFree<A>(value, release));
    }

    public IO<Unit> Release<A>(A value)

View on GitHub (pinned to 2f0e362824)