Problem Summary
When deserializing JSON containing a repeated enum field (List<SomeEnum>) with unknown enum values, Wire's GSON integration crashes with IllegalArgumentException: field_name.contains(null) because:
EnumJsonFormatter.fromString() returns null for unknown enum values
ListJsonAdapter adds these null values to the result list
- Wire's
immutableCopyOf() rejects lists containing null
Affected Code Paths
1. EnumJsonFormatter.kt (wire-runtime)
override fun fromString(value: String): E? {
return stringToValue[value]
// If the constant is unknown to our runtime, we return a `Unrecognized` instance if it has
// been generated.
?: unrecognizedClassConstructor?.newInstance(value.toInt())
}
When there's no Unrecognized class (standard enum generation), this returns null for unknown values.
2. GsonJsonIntegration.kt (wire-gson-support)
private class ListJsonAdapter<T>(
private val single: TypeAdapter<T>,
) : TypeAdapter<List<T?>>() {
override fun read(reader: JsonReader): List<T?> {
val result = mutableListOf<T?>()
reader.beginArray()
while (reader.hasNext()) {
result.add(single.read(reader)) // Adds null from EnumJsonFormatter
}
reader.endArray()
return result // List contains nulls!
}
}
3. Internal.kt (wire-runtime)
fun <T> immutableCopyOf(name: String, list: List<T>): List<T> {
if (list.contains(null)) {
throw IllegalArgumentException("$name.contains(null)") // CRASH!
}
// ...
}
Reproduction
Given this proto:
enum HotelPageBlock {
UNKNOWN = 0;
MAP_BLOCK = 1;
BENEFITS_BLOCK = 2;
}
message RubiricHotelPageBlockOrder {
repeated HotelPageBlock hotel_page_blocks = 1;
}
And this JSON with an unknown enum value (e.g., server added NEW_BLOCK = 17):
{
"hotelPageBlocks": ["MAP_BLOCK", "NEW_BLOCK", "BENEFITS_BLOCK"]
}
Deserialization crashes:
java.lang.IllegalArgumentException: hotel_page_blocks.contains(null)
at com.squareup.wire.internal.Internal__InternalKt.immutableCopyOf(Internal.kt:72)
at proto.hotels.v1.RubiricHotelPageBlockOrder.<init>(RubiricHotelPageBlockOrder.kt:48)
Expected Behavior
Wire should handle unknown enum values in repeated fields gracefully, either by:
- Filtering out unknown values (like proto binary decoding does - stores in
unknownFields)
- Using the default enum value (first constant, typically
UNKNOWN = 0)
- Providing a configuration option to choose the behavior
Problem Summary
When deserializing JSON containing a repeated enum field (
List<SomeEnum>) with unknown enum values, Wire's GSON integration crashes withIllegalArgumentException: field_name.contains(null)because:EnumJsonFormatter.fromString()returnsnullfor unknown enum valuesListJsonAdapteradds thesenullvalues to the result listimmutableCopyOf()rejects lists containingnullAffected Code Paths
1.
EnumJsonFormatter.kt(wire-runtime)When there's no
Unrecognizedclass (standard enum generation), this returnsnullfor unknown values.2.
GsonJsonIntegration.kt(wire-gson-support)3.
Internal.kt(wire-runtime)Reproduction
Given this proto:
And this JSON with an unknown enum value (e.g., server added
NEW_BLOCK = 17):{ "hotelPageBlocks": ["MAP_BLOCK", "NEW_BLOCK", "BENEFITS_BLOCK"] }Deserialization crashes:
Expected Behavior
Wire should handle unknown enum values in repeated fields gracefully, either by:
unknownFields)UNKNOWN = 0)