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
- Change the map key type in the .proto file to a scalar string/bool/integer type and regenerate the C# code
- If a float/bytes key is needed, restructure as a repeated message with key and value fields instead of a map
- Guard against null keys before formatting; sanitize or remove entries with null keys
- 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
- Only use string/bool/integer key types in proto map fields
- Never put arbitrary bytes or floats in map keys
- Validate runtime-built maps for null keys before formatting
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
- String contains low surrogate not followed by high surrogate
- String contains high surrogate not preceded by low surrogate
- Expected an object to populate a map
- Invalid map field:
- Map values must not be null
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)