louthy/language-ext · error · ArgumentException

Key already exists in map

Error message

Key already exists in map: {change.Key}

What it means

During a TrieMap update traversal, when the key is found and the update type is Add (not SetItem/TryAdd), the library throws because Add is defined as insert-only: adding an existing key is an error. TryAdd silently keeps the existing entry instead. This enforces the semantic difference between Add and SetItem on the immutable map.

Solutions

  1. Use AddOrUpdate(key, value) or SetItem when overwriting is intended
  2. Use TryAdd when you want to keep the first value and ignore duplicates
  3. Deduplicate or group the source sequence before adding
  4. Check ContainsKey before Add if the branch logic requires it

Example fix

// before
map = map.Add(key, value);
// after
map = map.AddOrUpdate(key, value);
Defensive patterns

Strategy: validation

Validate before calling

if (map.ContainsKey(key)) map = map.SetItem(key, value); else map = map.Add(key, value);

Try / catch

try { map = map.Add(key, value); }
catch (ArgumentException) { map = map.SetItem(key, value); }

Prevention

When it happens

Trigger: Calling map.Add(key, value) (or union/duplicate-preserving bulk adds that route UpdateType.Add) on a TrieMap that already contains an equal key under its EqK comparer.

Common situations: Building a map from user-supplied key/value pairs containing duplicates; merging two maps with overlapping keys using Add semantics; case differences that a case-sensitive comparer treats as duplicates of different intent.

Related errors


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

Appendix: source

Thrown at LanguageExt.Core/Immutable Collections/TrieMap/TrieMap.cs:2307

        public (int CountDelta, Node Node, V? Old, bool Changed) Update((UpdateType Type, bool Mutate) env, (K Key, V Value) change, uint hash, Sec section)
        {
            // var hashIndex = Bit.Get(hash, section);
            // var mask = Mask(hashIndex);
            var mask = (uint)(1 << (int)((hash & (uint)(Sec.Mask << section.Offset)) >> section.Offset));

            //if (Bit.Get(EntryMap, mask))
            if((EntryMap & mask) == mask)
            {
                //var entryIndex = Index(EntryMap, mask);
                var entryIndex   = BitCount((int)EntryMap & ((int)mask - 1));
                var currentEntry = Items[entryIndex];

                if (EqK.Equals(currentEntry.Key, change.Key))
                {
                    if (env.Type == UpdateType.Add)
                    {
                        // Key already exists - so it's an error to add again
                        throw new ArgumentException($"Key already exists in map: {change.Key}");
                    }
                    else if (env.Type == UpdateType.TryAdd)
                    {
                        // Already added, so we don't continue to try
                        return (0, this, default, false);
                    }

                    var (newItems, old) = SetItem(Items, entryIndex, change, env.Mutate);
                    return (0, new Entries(EntryMap, NodeMap, newItems, Nodes), old.Value, true);
                }
                else
                {
                    if (env.Type == UpdateType.SetItem)
                    {
                        // Key must already exist to set it
                        throw new ArgumentException($"Key already exists in map: {change.Key}");
                    }
                    else if (env.Type == UpdateType.TrySetItem)

View on GitHub (pinned to 2f0e362824)