LuckyPennySoftware/AutoMapper · error · AutoMapperMappingException

Multiple matching keys were found in the source dictionary f

Error message

Multiple matching keys were found in the source dictionary for destination member {member}.

What it means

When mapping from an IDictionary<string,object> source onto a typed destination, FromStringDictionaryMapper.MatchSource matches each destination member name against dictionary keys. After an exact-key miss it falls back to comparing s.Key.Trim() == name; if two or more surviving keys collide on the same member, the mapping is ambiguous and throws AutoMapperMappingException. The match is whitespace-trim based, not case-insensitive.

Source

Thrown at src/AutoMapper/Mappers/FromStringDictionaryMapper.cs:24

    private static readonly MethodInfo MapDynamicMethod = typeof(FromStringDictionaryMapper).GetStaticMethod(nameof(MapDynamic));
    public bool IsMatch(TypePair context) => typeof(StringDictionary).IsAssignableFrom(context.SourceType);
    public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap,
        Expression sourceExpression, Expression destExpression) =>
            Call(MapDynamicMethod, sourceExpression, destExpression.ToObject(), Constant(destExpression.Type), ContextParameter, Constant(profileMap));
    private static object MapDynamic(StringDictionary source, object boxedDestination, Type destinationType, ResolutionContext context, ProfileMap profileMap)
    {
        boxedDestination ??= ObjectFactory.CreateInstance(destinationType);
        int matchedCount = 0;
        foreach (var member in profileMap.CreateTypeDetails(destinationType).WriteAccessors)
        {
            var (value, count) = MatchSource(member.Name);
            if (count == 0)
            {
                continue;
            }
            if (count > 1)
            {
                throw new AutoMapperMappingException($"Multiple matching keys were found in the source dictionary for destination member {member}.", null, new TypePair(typeof(StringDictionary), destinationType));
            }
            var mappedValue = context.MapMember(member, value, boxedDestination);
            member.SetMemberValue(boxedDestination, mappedValue);
            matchedCount++;
        }
        if (matchedCount < source.Count)
        {
            MapInnerProperties();
        }
        return boxedDestination;
        (object Value, int Count) MatchSource(string name)
        {
            if (source.TryGetValue(name, out var value))
            {
                return (value, 1);
            }
            var matches = source.Where(s => s.Key.Trim() == name).Select(s => s.Value).ToArray();
            if (matches.Length == 1)

View on GitHub (pinned to dfa6dd587c)

Solutions

  1. Normalize the source dictionary before mapping: trim every key and remove or merge collisions so each member resolves uniquely.
  2. Supply exact (untrimmed) keys that equal destination member names so the fast path (count==1) is taken and the trim fallback is never reached.
  3. Use ConvertUsing / a custom value resolver to pick the intended value deterministically.
  4. Rename one of the colliding keys at the source or drop the unwanted duplicate.

Example fix

// before
var dict = new Dictionary<string,object>{ ["Name"]="a", [" Name "]="b" };
var dest = mapper.Map<Dest>(dict); // ambiguous for Name

// after
var dict = new Dictionary<string,object>(StringComparer.Ordinal);
foreach (var kv in rawDict) dict[kv.Key.Trim()] = kv.Value; // de-dup/trim first
var dest = mapper.Map<Dest>(dict);
Defensive patterns

Strategy: validation

Validate before calling

var collisions = source
    .GroupBy(kv => kv.Key.Trim(), StringComparer.Ordinal)
    .Where(g => g.Count() > 1)
    .Select(g => g.Key)
    .ToList();
if (collisions.Count > 0)
    throw new ArgumentException($"Ambiguous source keys after trim: {string.Join(", ", collisions)}");

var dest = mapper.Map<Dest>(source);

Try / catch

try { dest = mapper.Map<Dest>(source); }
catch (AutoMapperMappingException ex) when (ex.Message.Contains("Multiple matching keys"))
{
    // log the failing member, normalize/dedup the dictionary, then retry
}

Prevention

When it happens

Trigger: Source dictionary contains multiple keys that are equal after .Trim() and there is no exact match for the destination member name, e.g. {"Name": x, " Name ": y} mapping to a Name property. Also two keys like "Id " and " Id" both trimming to "Id" with no exact "Id" entry.

Common situations: Dirty/expired dictionary payloads from CSV/JSON import; manually built dictionaries with accidental leading/trailing whitespace; dedup logic that merged values but left duplicate-ish keys; integration with a source system that pads keys.


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