louthy/language-ext · error · NotSupportedException
You can't map a tail call
Error message
You can't map a tail call
What it means
IOTail<A> is an internal marker node in the LanguageExt IO DSL representing a tail-recursive call produced by the IO monad's trampoline. It is not a runnable effect, so Map cannot legally wrap it — mapping over a tail call would break the tail-call resolution that the trampoline performs in `resolve`. The library throws NotSupportedException deliberately to surface misuse of the DSL internals early instead of silently corrupting the recursion.
Solutions
- Do not call Map directly on the IOTail node; resolve the tail call first (via the trampoline / IO.run) and map on the resolved IO.
- Pattern-match: if you have IOTail<A> t and need IO<B>, use t.Tail (the wrapped IO<A>) and Map over that, ensuring typeof(A)==typeof(B) constraints of resolve are respected.
- Restructure your recursive IO so the transformation is applied inside the recursive step (e.g. map inside the Bind continuation) rather than over the tail-call boundary.
- If you are inspecting the AST, skip or unwrap IOTail nodes explicitly before applying combinators.
Example fix
// before
IO<int> mapped = tailResult.Map(x => x * 2); // throws
// after
IO<int> resolved = tailResult switch
{
IOTail<int> t => t.Tail, // unwrap the tail call
var io => io
};
IO<int> mapped = resolved.Map(x => x * 2); Defensive patterns
Strategy: type-guard
Validate before calling
bool isMappable<A>(IO<A> io) => io is not IOTail<A>;
Type guard
IO<B> safeMap<A,B>(IO<A> io, Func<A,B> f) =>
io is IOTail<A> t ? t.Tail.Map(f) : io.Map(f); Try / catch
try { result = io.Map(f); }
catch (NotSupportedException ex) when (ex.Message.Contains("tail call"))
{
result = ((IOTail<A>)io).Tail.Map(f);
} Prevention
- Never apply combinators directly to nodes from LanguageExt.DSL; work with resolved IO values.
- Add an IOTail type-check branch before generic Map calls in interpreter code.
- Keep transformations inside recursive steps rather than outside tail-recursive loops.
When it happens
Trigger: Calling `.Map(f)` on an IO value that is actually an IOTail<A> — typically only reachable if you pattern-match into IO internals (DSL namespace) or call Map on the result of a tail-recursive `go` function before the trampoline resolves it.
Common situations: Developers writing custom IO interpreters or traversing the IO AST in LanguageExt.DSL; upgrading LanguageExt versions where IOTail became part of the public-ish DSL surface and code that previously saw IO monads now sees IOTail nodes.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- You can't chain a tail call
- Tail calls can't transform in the `select`
- Invalid iterator
- Ord attribute should have a struct type that derives from…
- Hashable attribute should have a struct type that derives…
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/49117818ab933d33.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/Effects/IO/DSL/IOTail.cs:10
using System;
using System.Threading.Tasks;
using LanguageExt.Traits;
namespace LanguageExt.DSL;
record IOTail<A>(IO<A> Tail) : IO<A>
{
public override IO<B> Map<B>(Func<A, B> f) =>
throw new NotSupportedException("You can't map a tail call");
public override IO<B> Bind<B>(Func<A, K<IO, B>> f) =>
throw new NotSupportedException("You can't chain a tail call");
public override IO<B> BindAsync<B>(Func<A, ValueTask<K<IO, B>>> f) =>
throw new NotSupportedException("You can't chain a tail call");
public override string ToString() =>
"IO tail";
public static IO<C> resolve<B, C>(A initialValue, IO<B> bindResult, Func<A, B, C> project)
=> bindResult switch
{
IOTail<B> tail when typeof(B) == typeof(C) => (IO<C>)(object)tail.Tail,
IOTail<B> => throw new NotSupportedException("Tail calls can't transform in the `select`"),
var mb => mb.Map(y => project(initialValue, y))
};
}View on GitHub (pinned to 2f0e362824)