XINCGer/Unity3DTraining · error · ArgumentException

Dictionary has entry with null key

Error message

Dictionary has entry with null key

What it means

JsonFormatter.WriteDictionary throws ArgumentException when formatting a map field whose dictionary contains a null key. Protobuf map keys are scalars and can never be null; the formatter refuses to serialize such an entry.

Solutions

  1. Remove or reject null keys before adding entries to the map field
  2. Validate the source dictionary: any k => k == null entries must be handled
  3. Normalize null keys to a sentinel value only if legal for your schema
  4. Add a guard before JsonFormatter.Format when maps originate from external data

Example fix

// before
mapMsg.Fields[keyFromUser] = v; // keyFromUser may be null
// after
if (keyFromUser != null) { mapMsg.Fields[keyFromUser] = v; }
Defensive patterns

Strategy: validation

Validate before calling

if (sourceDict.Keys.Any(k => k == null)) { throw new ArgumentException("Source dictionary contains null keys"); }

Type guard

static bool HasNullKey<TKey,TValue>(Dictionary<TKey,TValue> d) where TKey : class => d.Keys.Any(k => k == null);

Try / catch

try { json = formatter.Format(msg); } catch (ArgumentException ex) when (ex.Message.Contains("null key")) { /* drop/fix null-key entries and retry */ }

Prevention

When it happens

Trigger: A C# Dictionary used as a protobuf map field has a null key inserted (possible for string-keyed maps) and the message is then formatted to JSON.

Common situations: Building map fields from user-supplied or external dictionaries that permit null keys; deserializing exotic formats into maps; LINQ projections producing null keys.

Related errors


AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12). Data as JSON: /api/errors/87db9f1f9642ede1. Report an issue: GitHub.

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonFormatter.cs:666

                }
                string keyText;
                if (pair.Key is string)
                {
                    keyText = (string)pair.Key;
                }
                else if (pair.Key is bool)
                {
                    keyText = (bool)pair.Key ? "true" : "false";
                }
                else if (pair.Key is int || pair.Key is uint | pair.Key is long || pair.Key is ulong)
                {
                    keyText = ((IFormattable)pair.Key).ToString("d", CultureInfo.InvariantCulture);
                }
                else
                {
                    if (pair.Key == null)
                    {
                        throw new ArgumentException("Dictionary has entry with null key");
                    }
                    throw new ArgumentException("Unhandled dictionary key type: " + pair.Key.GetType());
                }
                WriteString(writer, keyText);
                writer.Write(NameValueSeparator);
                WriteValue(writer, pair.Value);
                first = false;
            }
            writer.Write(first ? "}" : " }");
        }

        /// <summary>
        /// Writes a string (including leading and trailing double quotes) to a builder, escaping as required.
        /// </summary>
        /// <remarks>
        /// Other than surrogate pair handling, this code is mostly taken from src/google/protobuf/util/internal/json_escaping.cc.
        /// </remarks>
        internal static void WriteString(TextWriter writer, string text)

View on GitHub (pinned to 016f98412e)