google/gson · error · MalformedJsonException
Custom JsonElement subclass {className} is not supported
Error message
Custom JsonElement subclass {className} is not supported What it means
JsonTreeReader.peek throws MalformedJsonException('Custom JsonElement subclass ' + className + ' is not supported') when the top of the stack is an object that is not Iterator, JsonObject, JsonArray, JsonPrimitive, JsonNull, or the closed sentinel — i.e. a user-defined JsonElement subclass that the tree reader cannot classify. The {className} identifies the offending class.
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 8b8628c656)
Solutions
- Avoid subclassing JsonElement; use only JsonObject, JsonArray, JsonPrimitive, JsonNull in trees you hand to JsonTreeReader/Gson.
- Normalize any custom element to a standard JsonObject before constructing a JsonTreeReader.
- If you control the source, replace custom elements with their plain equivalents.
- Register a custom TypeAdapter if you truly need a non-standard element shape, and skip JsonTreeReader.
Example fix
// before
class Tagged extends JsonElement { /* custom */ }
JsonObject root = new JsonObject(); root.add("x", new Tagged());
new JsonTreeReader(root).peek(); // MalformedJsonException
// after: store plain JsonPrimitive / JsonObject
JsonObject root = new JsonObject(); root.addProperty("x", 1);
new JsonTreeReader(root).peek(); // BEGIN_OBJECT Defensive patterns
Strategy: type-guard
Validate before calling
// Walk the tree and reject custom JsonElement subclasses before constructing a reader
static void assertStandard(JsonElement e) {
if (e instanceof JsonObject) { ((JsonObject) e).values().forEach(JsonGuard::assertStandard); }
else if (e instanceof JsonArray) { ((JsonArray) e).forEach(JsonGuard::assertStandard); }
else if (!(e instanceof JsonPrimitive) && !(e instanceof JsonNull)) {
throw new IllegalArgumentException("Unsupported JsonElement: " + e.getClass());
}
} Type guard
static boolean isStandardJsonElement(Object o) {
return o instanceof JsonObject || o instanceof JsonArray
|| o instanceof JsonPrimitive || o instanceof JsonNull;
} Try / catch
try {
new JsonTreeReader(element).peek();
} catch (MalformedJsonException e) {
if (e.getMessage() != null && e.getMessage().contains("Custom JsonElement subclass")) {
throw new IllegalArgumentException("Tree contains a custom JsonElement subclass", e);
}
throw e;
} Prevention
- Use only the four standard JsonElement types in trees.
- Normalize custom element types at the boundary before parsing.
- Audit third-party code that produces JsonElement trees.
When it happens
Trigger: Constructing a JsonTreeReader over a JsonElement tree that contains a custom JsonElement subclass (one that fails the instanceof checks for JsonObject/JsonArray/JsonPrimitive/JsonNull). Happens when a custom JsonElement type sneaks into a tree that is then parsed back through JsonTreeReader.
Common situations: User JsonElement subclasses carrying metadata; adapters that splice non-standard element types into a JsonObject; libraries that extend JsonObject but break instanceof identity.
Related errors
- Couldn't write {class}
- Primitive is neither a number nor a string
- String value is empty
- Unexpected token: {peeked}
- JsonReader is closed
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/5f4b5132b24805d1.json.
Report an issue: GitHub.