google/gson · error · IllegalArgumentException
Type adapter ${className} must implement JsonSerializer or J
Error message
Type adapter ${className} must implement JsonSerializer or JsonDeserializer What it means
TreeTypeAdapter.SingleTypeFactory wraps an object as a JsonSerializer and/or JsonDeserializer. If the object implements neither interface, the factory has no serialization or deserialization logic and rejects it with IllegalArgumentException. Through GsonBuilder.registerTypeAdapter a separate pre-check fires first, so this specific message surfaces when the factory is created directly.
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 310ac341f2)
Solutions
- If the object is a JsonSerializer or JsonDeserializer, ensure it correctly implements the interface generics
- If the object is a TypeAdapter, use TypeAdapters.newFactory instead of TreeTypeAdapter.newFactory
- Route adapter registration through GsonBuilder.registerTypeAdapter which validates and dispatches to the correct factory type
Example fix
// before — TypeAdapter passed to TreeTypeAdapter factory TypeAdapter<MyType> adapter = ...; factory = TreeTypeAdapter.newFactory(TypeToken.get(MyType.class), adapter); // throws // after — use the correct factory for TypeAdapter factory = TypeAdapters.newFactory(TypeToken.get(MyType.class), adapter);
Defensive patterns
Strategy: validation
Validate before calling
// Validate before creating a TreeTypeAdapter factory
public static void validateAdapter(Object adapter) {
if (!(adapter instanceof JsonSerializer<?>) && !(adapter instanceof JsonDeserializer<?>)) {
throw new IllegalArgumentException(
adapter.getClass().getName() + " must implement JsonSerializer or JsonDeserializer");
}
} Prevention
- Route all adapter registration through GsonBuilder.registerTypeAdapter which validates the interface
- For TypeAdapter objects, use TypeAdapters.newFactory, not TreeTypeAdapter.newFactory
- Verify adapter classes implement the correct Gson interface before passing to any factory API
When it happens
Trigger: Directly calling TreeTypeAdapter.newFactory, newFactoryWithMatchRawType, or newTypeHierarchyFactory with an object that does not implement JsonSerializer or JsonDeserializer; passing a TypeAdapter or plain object to a TreeTypeAdapter factory API instead of the TypeAdapter factory API.
Common situations: Library or framework code that programmatically constructs TreeTypeAdapter factories; misuse of the internal TreeTypeAdapter API instead of GsonBuilder.registerTypeAdapter.
Related errors
- Class ${typeAdapter.getClass().getName()} does not implement
- GSON (${GsonBuildConfig.VERSION}) cannot handle ${type}
- Cannot override built-in adapter for ${type}
- Invalid attempt to bind an instance of ${className} as a @Js
- types and labels must be unique
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/4c6406035df3d479.
Report an issue: GitHub.