{"record":{"id":"0e821693b672c609","repo":"LuckyPennySoftware/AutoMapper","slug":"multiple-matching-keys-were-found-in-the-source-di","errorCode":null,"errorMessage":"Multiple matching keys were found in the source dictionary for destination member {member}.","messagePattern":"Multiple matching keys were found in the source dictionary for destination member (.+?)\\.","errorType":"exception","errorClass":"AutoMapperMappingException","httpStatus":null,"severity":"error","filePath":"src/AutoMapper/Mappers/FromStringDictionaryMapper.cs","lineNumber":24,"sourceCode":"    private static readonly MethodInfo MapDynamicMethod = typeof(FromStringDictionaryMapper).GetStaticMethod(nameof(MapDynamic));\n    public bool IsMatch(TypePair context) => typeof(StringDictionary).IsAssignableFrom(context.SourceType);\n    public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap,\n        Expression sourceExpression, Expression destExpression) =>\n            Call(MapDynamicMethod, sourceExpression, destExpression.ToObject(), Constant(destExpression.Type), ContextParameter, Constant(profileMap));\n    private static object MapDynamic(StringDictionary source, object boxedDestination, Type destinationType, ResolutionContext context, ProfileMap profileMap)\n    {\n        boxedDestination ??= ObjectFactory.CreateInstance(destinationType);\n        int matchedCount = 0;\n        foreach (var member in profileMap.CreateTypeDetails(destinationType).WriteAccessors)\n        {\n            var (value, count) = MatchSource(member.Name);\n            if (count == 0)\n            {\n                continue;\n            }\n            if (count > 1)\n            {\n                throw new AutoMapperMappingException($\"Multiple matching keys were found in the source dictionary for destination member {member}.\", null, new TypePair(typeof(StringDictionary), destinationType));\n            }\n            var mappedValue = context.MapMember(member, value, boxedDestination);\n            member.SetMemberValue(boxedDestination, mappedValue);\n            matchedCount++;\n        }\n        if (matchedCount < source.Count)\n        {\n            MapInnerProperties();\n        }\n        return boxedDestination;\n        (object Value, int Count) MatchSource(string name)\n        {\n            if (source.TryGetValue(name, out var value))\n            {\n                return (value, 1);\n            }\n            var matches = source.Where(s => s.Key.Trim() == name).Select(s => s.Value).ToArray();\n            if (matches.Length == 1)","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/LuckyPennySoftware/AutoMapper/blob/dfa6dd587c5854b4beee5934beb39ba6e9569b84/src/AutoMapper/Mappers/FromStringDictionaryMapper.cs#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Normalize the source dictionary before mapping: trim every key and remove or merge collisions so each member resolves uniquely.","Supply exact (untrimmed) keys that equal destination member names so the fast path (count==1) is taken and the trim fallback is never reached.","Use ConvertUsing / a custom value resolver to pick the intended value deterministically.","Rename one of the colliding keys at the source or drop the unwanted duplicate."],"exampleFix":"// before\nvar dict = new Dictionary<string,object>{ [\"Name\"]=\"a\", [\" Name \"]=\"b\" };\nvar dest = mapper.Map<Dest>(dict); // ambiguous for Name\n\n// after\nvar dict = new Dictionary<string,object>(StringComparer.Ordinal);\nforeach (var kv in rawDict) dict[kv.Key.Trim()] = kv.Value; // de-dup/trim first\nvar dest = mapper.Map<Dest>(dict);","handlingStrategy":"validation","validationCode":"var collisions = source\n    .GroupBy(kv => kv.Key.Trim(), StringComparer.Ordinal)\n    .Where(g => g.Count() > 1)\n    .Select(g => g.Key)\n    .ToList();\nif (collisions.Count > 0)\n    throw new ArgumentException($\"Ambiguous source keys after trim: {string.Join(\", \", collisions)}\");\n\nvar dest = mapper.Map<Dest>(source);","typeGuard":null,"tryCatchPattern":"try { dest = mapper.Map<Dest>(source); }\ncatch (AutoMapperMappingException ex) when (ex.Message.Contains(\"Multiple matching keys\"))\n{\n    // log the failing member, normalize/dedup the dictionary, then retry\n}","preventionTips":["Normalize and trim dictionary keys at the boundary where the dictionary enters your code.","Use a case-sensitive ordinal comparer and disallow duplicate keys upstream.","Add a destination member name exact-match key so the trim fallback is never exercised."],"tags":["dictionary","dynamic-mapping","data-collision","string-dictionary"],"backgroundTag":null,"analyzedSha":"dfa6dd587c5854b4beee5934beb39ba6e9569b84","analyzedAt":"2026-08-13T19:56:33.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}