google/gson · error · IllegalArgumentException
Type adapter " + typeAdapter.getClass().getName() + " must i
Error message
Type adapter " + typeAdapter.getClass().getName() + " must implement JsonSerializer or JsonDeserializer
What it means
Thrown by TreeTypeAdapter.SingleTypeFactory when an object registered as a type adapter (via TreeTypeAdapter.newFactory / newTypeHierarchyFactory) implements neither JsonSerializer nor JsonDeserializer. Gson requires the legacy 'tree adapter' object to implement at least one of these interfaces; an IllegalArgumentException is raised at factory construction time (often when Gson is built).
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java:167
Class<?> hierarchyType, Object typeAdapter) {
return new SingleTypeFactory(typeAdapter, null, false, hierarchyType);
}
private static final class SingleTypeFactory implements TypeAdapterFactory {
private final TypeToken<?> exactType;
private final boolean matchRawType;
private final Class<?> hierarchyType;
private final JsonSerializer<?> serializer;
private final JsonDeserializer<?> deserializer;
SingleTypeFactory(
Object typeAdapter, TypeToken<?> exactType, boolean matchRawType, Class<?> hierarchyType) {
serializer = typeAdapter instanceof JsonSerializer ? (JsonSerializer<?>) typeAdapter : null;
deserializer =
typeAdapter instanceof JsonDeserializer ? (JsonDeserializer<?>) typeAdapter : null;
if (serializer == null && deserializer == null) {
Objects.requireNonNull(typeAdapter);
throw new IllegalArgumentException(
"Type adapter "
+ typeAdapter.getClass().getName()
+ " must implement JsonSerializer or JsonDeserializer");
}
this.exactType = exactType;
this.matchRawType = matchRawType;
this.hierarchyType = hierarchyType;
}
@SuppressWarnings("unchecked") // guarded by typeToken.equals() call
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
boolean matches =
exactType != null
? exactType.equals(type) || (matchRawType && exactType.getType() == type.getRawType())
: hierarchyType.isAssignableFrom(type.getRawType());
return matches
? new TreeTypeAdapter<>(View on GitHub (pinned to 8b8628c656)
Solutions
- Make the registered object implement JsonSerializer<T> and/or JsonDeserializer<T>.
- If it is a modern TypeAdapter, use GsonBuilder.registerTypeAdapter (or a TypeAdapterFactory) instead of TreeTypeAdapter.newFactory.
- Double-check the generic type parameter matches the TypeToken passed to newFactory.
Example fix
// before
TreeTypeAdapter.newFactory(TypeToken.get(Foo.class), new FooTypeAdapter());
// FooTypeAdapter extends TypeAdapter<Foo> only -> throws
// after: implement the legacy interfaces, or register as TypeAdapter
class FooSerializer implements JsonSerializer<Foo> {
public JsonElement serialize(Foo src, Type t, JsonSerializationContext c) {
return c.serialize(src.name);
}
}
// or simpler: gsonBuilder.registerTypeAdapter(Foo.class, new FooTypeAdapter()); Defensive patterns
Strategy: validation
Validate before calling
// Verify the adapter object implements a supported interface at registration time
Object adapter = new FooTypeAdapter();
if (!(adapter instanceof JsonSerializer) && !(adapter instanceof JsonDeserializer)) {
throw new IllegalArgumentException(adapter.getClass().getName()
+ " must implement JsonSerializer or JsonDeserializer for TreeTypeAdapter.newFactory");
}
TreeTypeAdapter.newFactory(TypeToken.get(Foo.class), adapter); Type guard
// Guard: prefer the modern TypeAdapter registration path when applicable
static boolean isTreeAdapterCompatible(Object o) {
return o instanceof JsonSerializer || o instanceof JsonDeserializer;
} Try / catch
null
Prevention
- Use GsonBuilder.registerTypeAdapter for modern TypeAdapter instances; reserve TreeTypeAdapter factories for JsonSerializer/JsonDeserializer.
- Add a startup check that all custom factories build successfully.
- Document the interface requirement on adapter helpers.
When it happens
Trigger: Calling TreeTypeAdapter.newFactory(TypeToken, Object), newFactoryWithMatchRawType, or newTypeHierarchyFactory with an object that is a plain TypeAdapter (new style) or arbitrary class. The factory at line 165 detects serializer==null && deserializer==null and throws.
Common situations: Migrating from old Gson where adapters were JsonSerializer/JsonDeserializer to the modern TypeAdapter API and passing the wrong kind of object into a tree factory; copy-paste errors; passing a lambda that does not implement the right interface; accidental null-like adapter wrappers.
Related errors
- Failed to invoke constructor '" + ReflectionHelper.construct
- Attempted to serialize java.lang.Class: " + value.getName()
- Invalid style: {style}
- Class {typeAdapter.getClass().getName()} does not implement
- Cannot override built-in adapter for {type}
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/378090e77fd03ae5.json.
Report an issue: GitHub.