louthy/language-ext · info · OperationCanceledException

OperationCanceledException

Error message

OperationCanceledException

What it means

IO.cancel (IO.Prelude) is an IO<Unit> that cancels the ambient CancellationTokenSource and then throws OperationCanceledException. It is the library's intended way to abort an IO computation; the exception is the cancellation signal, not a bug.

Solutions

  1. If cancellation is intended, catch OperationCanceledException (or use IO.Catch with a cancellation filter) at the top of the flow
  2. Check Token.IsCancellationRequested via the IO environment instead of hard-throwing when you want graceful shutdown
  3. Ensure bracket/finalizer logic (Bracket/BracketFail) is used so resources are released on cancellation
  4. Do not swallow OperationCanceledException generically with catch(Exception); rethrow or filter it

Example fix

// before
var io = work.Bind(_ => IO.cancel); // unwinds with OperationCanceledException
// after
var io = work.Bind(_ => IO.cancel).Catch(OperationCanceledException (_)=> IO.unit); // or handle at boundary
Defensive patterns

Strategy: try-catch

Validate before calling

// No pre-check possible: evaluating IO.cancel always throws; branch conditionally instead
if (shouldStop) { /* choose a non-throwing IO path */ }

Type guard

bool IsCancelled(Exception e) => e is OperationCanceledException;

Try / catch

try { result = io.Run(env); }
catch (OperationCanceledException) { /* expected cancellation path */ }

Prevention

When it happens

Trigger: Evaluating the `cancel` IO value, directly or as part of a larger IO chain (e.g. io.Bind(_ => IO.cancel)), which calls e.Source.Cancel() and throws OperationCanceledException.

Common situations: Deliberately aborting long-running IO flows, wiring a cancellation branch into a selective/awaitAny-style composition, or accidentally including `cancel` in a pipeline that then unwinds.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Effects/IO/Prelude/IO.Prelude.cs:30

public static partial class Prelude
{
    /// <summary>
    /// Access the cancellation-token from the IO environment
    /// </summary>
    /// <returns>CancellationToken</returns>
    public static readonly IO<CancellationToken> cancelToken =
        IO.lift(e => e.Token);

    /// <summary>
    /// Request a cancellation of the IO expression
    /// </summary>
    public static readonly IO<Unit> cancel =
        IO.lift<Unit>(
            e =>
            {
                e.Source.Cancel();
                throw new OperationCanceledException();
            });
    
    /// <summary>
    /// Always yields a `Unit` value
    /// </summary>
    public static readonly IO<Unit> unitIO = 
        IO.pure<Unit>(default);
    
    /// <summary>
    /// Yields the IO environment
    /// </summary>
    public static readonly IO<EnvIO> envIO = 
        IO.lift<EnvIO>(e => e);

    /// <summary>
    /// Tail call 
    /// </summary>
    /// <param name="tailIO"></param>

View on GitHub (pinned to 2f0e362824)