LuckyPennySoftware/AutoMapper · error · InvalidOperationException

Context.Items are only available when using a Map overload…

Error message

Context.Items are only available when using a Map overload that takes Action<IMappingOperationOptions>! Consider using Context.TryGetItems instead.

What it means

ResolutionContext.Items is only backed by storage when the top-level Map call passed an Action<IMappingOperationOptions> (where items are set). In a default/empty context (e.g. a nested call or a resolver invoked from a Map overload without options), accessing Items calls CheckDefault which throws InvalidOperationException and suggests TryGetItems. The library deliberately refuses to silently hand back an unavailable dictionary.

Solutions

  1. Replace context.Items with context.TryGetItems(out var items) and handle the null/items-missing case gracefully.
  2. At call sites that need items, use mapper.Map<S,D>(src, opts => opts.Items["key"] = value) so a non-default context is in effect.
  3. Make item consumption optional and fall back to a sensible default when items are absent.
  4. Audit every resolver/converter to ensure none assume Items is always populated.

Example fix

// before
public int Resolve(Source s, Dest d, int dest, ResolutionContext ctx) => (int)ctx.Items["factor"];
var dto = mapper.Map<Dest>(src); // throws: Items unavailable

// after
public int Resolve(Source s, Dest d, int dest, ResolutionContext ctx) =>
    ctx.TryGetItems(out var items) && items.TryGetValue("factor", out var f) ? (int)f : 1;
var dto = mapper.Map<Dest>(src);
Defensive patterns

Strategy: validation

Validate before calling

// Always probe with TryGetItems before touching Items:
if (context.TryGetItems(out var items) && items.TryGetValue("factor", out var f))
{
    factor = (int)f;
}
else
{
    factor = 1; // safe default when no options callback was supplied
}

Prevention

When it happens

Trigger: Reading context.Items["key"] from a value resolver, type converter, BeforeMap/AfterMap, or custom mapper when the outer Map used the simple overload mapper.Map<S,D>(src) with no options callback, leaving IsDefault true.

Common situations: A resolver reads Items but some call sites use the parameterless Map overload; a refactor dropped the options callback; unit-testing a resolver in isolation; items that were assumed to always be present.


AI-assisted analysis of LuckyPennySoftware/AutoMapper@dfa6dd587c (2026-08-13). Data as JSON: /api/errors/333ec571cb90982e. Report an issue: GitHub.

Appendix: source

Thrown at src/AutoMapper/ResolutionContext.cs:110

    internal static void CheckContext(ref ResolutionContext resolutionContext)
    {
        if (resolutionContext.IsDefault)
        {
            resolutionContext = new(resolutionContext._mapper);
        }
    }
    internal TDestination MapInternal<TSource, TDestination>(TSource source, TDestination destination, MemberMap memberMap) =>
        _mapper.Map(source, destination, this, memberMap: memberMap);
    internal object Map(object source, object destination, Type sourceType, Type destinationType, MemberMap memberMap) =>
        _mapper.Map(source, destination, this, sourceType, destinationType, memberMap);
    private void CheckDefault()
    {
        if (IsDefault)
        {
            ThrowInvalidMap();
        }
    }
    private static void ThrowInvalidMap() => throw new InvalidOperationException("Context.Items are only available when using a Map overload that takes Action<IMappingOperationOptions>! Consider using Context.TryGetItems instead.");
}
public readonly record struct ContextCacheKey(object Source, Type DestinationType)
{
    public override int GetHashCode() => HashCode.Combine(DestinationType, RuntimeHelpers.GetHashCode(Source));
    public bool Equals(ContextCacheKey other) => DestinationType == other.DestinationType && Source == other.Source;
}

View on GitHub (pinned to dfa6dd587c)