google/gson · error · MalformedJsonException
Custom JsonElement subclass ${className} is not supported
Error message
Custom JsonElement subclass ${className} is not supported What it means
JsonTreeReader only recognizes JsonObject, JsonArray, JsonPrimitive, and JsonNull on its stack. When peek encounters an object that is none of these (a user-defined JsonElement subclass), it throws MalformedJsonException. This guards the tree-walk against types Gson never produces itself.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java:168
} else if (o instanceof JsonArray) {
return JsonToken.BEGIN_ARRAY;
} else if (o instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) o;
if (primitive.isString()) {
return JsonToken.STRING;
} else if (primitive.isBoolean()) {
return JsonToken.BOOLEAN;
} else if (primitive.isNumber()) {
return JsonToken.NUMBER;
} else {
throw new AssertionError();
}
} else if (o instanceof JsonNull) {
return JsonToken.NULL;
} else if (o == SENTINEL_CLOSED) {
throw new IllegalStateException("JsonReader is closed");
} else {
throw new MalformedJsonException(
"Custom JsonElement subclass " + o.getClass().getName() + " is not supported");
}
}
private Object peekStack() {
return stack[stackSize - 1];
}
@CanIgnoreReturnValue
private Object popStack() {
Object result = stack[--stackSize];
stack[stackSize] = null;
return result;
}
private void expect(JsonToken expected) throws IOException {
if (peek() != expected) {
throw new IllegalStateException(View on GitHub (pinned to 310ac341f2)
Solutions
- Do not subclass JsonElement; use JsonObject/JsonArray/JsonPrimitive/JsonNull directly.
- Convert the custom element to a JsonObject before parsing.
- Register a TypeAdapter that handles your custom type instead of relying on tree-reading.
Example fix
// before
class MyObj : JsonElement() { ... }
val reader = JsonTreeReader(MyObj())
reader.peek() // Custom JsonElement subclass is not supported
// after: use standard JsonObject
val reader = JsonTreeReader(JsonObject().apply { addProperty("k", 1) })
reader.peek() Defensive patterns
Strategy: type-guard
Validate before calling
// Reject custom JsonElement subclasses before building a tree reader
static boolean isSupportedTreeElement(JsonElement e) {
return e == null || e.isJsonNull() || e.isJsonObject() || e.isJsonArray() || e.isJsonPrimitive();
}
if (!isSupportedTreeElement(root)) throw new IllegalArgumentException("Unsupported element");
new JsonTreeReader(root).peek(); Type guard
static boolean isStandardJsonElement(JsonElement e) {
return e instanceof JsonObject || e instanceof JsonArray
|| e instanceof JsonPrimitive || e instanceof JsonNull;
} Try / catch
try {
new JsonTreeReader(element).peek();
} catch (MalformedJsonException e) {
if (e.getMessage().startsWith("Custom JsonElement subclass")) {
// convert to JsonObject and retry
new JsonTreeReader(toJsonObject(element)).peek();
} else throw e;
} Prevention
- Never subclass JsonElement; use the four standard subtypes.
- Validate element types before constructing a JsonTreeReader.
- Register a TypeAdapter for custom element-like types.
- Add a guard in test fixtures that builds JsonElement trees.
When it happens
Trigger: Constructing a JsonTreeReader (directly or by passing a JsonElement to Gson) whose root or nested element is an instance of a custom JsonElement subclass that does not extend one of the four standard types.
Common situations: Subclassing JsonElement/JsonObject and feeding instances into gson.fromJson(element, type); libraries that introduce proxy JsonElement types.
Related errors
- Couldn't write ${class}
- Unexpected ${peeked} when reading a JsonElement.
- Unexpected token: ${peeked}
- JsonReader is closed
- Expected ${expected} but was ${peek} at path ${path}
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/009ac5c7a9171aae.
Report an issue: GitHub.