XINCGer/Unity3DTraining · error · ArgumentException

Unhandled dictionary key type:

Error message

Unhandled dictionary key type: 

What it means

Google.Protobuf's JsonFormatter can only serialize dictionary keys that are strings, booleans, or integers (it converts them to a JSON string key). If a message contains a map field whose key type has no JSON representation (e.g. float, double, bytes, or message keys), WriteDictionary throws this ArgumentException. It also throws when a dictionary entry has a null key.

Solutions

  1. Change the map key type in the .proto file to a scalar string/bool/integer type and regenerate the C# code
  2. If a float/bytes key is needed, restructure as a repeated message with key and value fields instead of a map
  3. Guard against null keys before formatting; sanitize or remove entries with null keys
  4. Use JsonFormatter with diagnostics or pre-convert unsupported keys to strings before serialization

Example fix

// before
message Config {
  map<float, string> thresholds = 1; // unsupported key type
}
// after
message Config {
  map<string, string> thresholds = 1; // keys as strings, e.g. "0.5"
}
Defensive patterns

Strategy: validation

Validate before calling

static bool IsSerializableKey(object k) => k != null && (k is string || k is bool || k is int || k is long || k is uint || k is ulong);

Type guard

static bool IsSerializableKey(object k) => k != null && (k is string || k is bool || k is int || k is long || k is uint || k is ulong);

Try / catch

try { formatter.Format(message); } catch (ArgumentException ex) when (ex.Message.StartsWith("Unhandled dictionary key type")) { log.Error("Map field has unsupported key type", ex); throw new SerializationFailureException(ex); }

Prevention

When it happens

Trigger: Serializing (JsonFormatter.Format / WriteTo) a proto message containing a map field whose key type is not string/bool/int32/int64/uint32/uint64 — e.g. map<float, string> or map<bytes, X> — or a map instance built in code with a null key.

Common situations: Hand-written .proto files using unsupported key types (float/double/bytes keys are not allowed by proto spec but may appear via reflection or dynamic messages); runtime-constructed maps with null keys; switching key type of a map field during a schema migration.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

                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)
        {
            writer.Write('"');

View on GitHub (pinned to 016f98412e)