google/gson · error · IllegalArgumentException
Couldn't write
Error message
Couldn't write ${class} What it means
JsonElementTypeAdapter.write handles null/JsonNull, JsonPrimitive, JsonArray, JsonObject. The final else branch throws IllegalArgumentException when the value is a JsonElement subclass that reports none of these (e.g. isJsonNull/isJsonPrimitive/isJsonArray/isJsonObject all return false), which happens with custom JsonElement subclasses.
Solutions
- Avoid subclassing JsonElement; favor composition over inheritance.
- Register a custom TypeAdapter for your JsonElement subclass via GsonBuilder.registerTypeAdapter.
- Convert the custom element to a standard JsonObject before serializing.
- If you must subclass, ensure isJsonPrimitive/isJsonArray/isJsonObject/isJsonNull behave correctly.
Example fix
// before
class MyElement : JsonElement() { ... }
gson.toJson(MyElement()) // Couldn't write MyElement
// after: register an adapter, or convert
gson.toJson(MyElement().toStandardObject()) Defensive patterns
Strategy: type-guard
Validate before calling
// Convert custom JsonElement to a standard JsonObject before serializing
static JsonElement normalize(JsonElement e) {
if (e.isJsonObject() || e.isJsonArray() || e.isJsonPrimitive() || e.isJsonNull()) return e;
JsonObject o = new JsonObject();
o.add("data", new JsonPrimitive(e.toString()));
return o;
}
Streams.write(normalize(customElement), writer); Type guard
static boolean isStandardJsonElement(JsonElement e) {
return e == null || e.isJsonNull() || e.isJsonObject() || e.isJsonArray() || e.isJsonPrimitive();
} Try / catch
try {
Streams.write(element, writer);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Couldn't write ")) {
// custom JsonElement subclass; convert and retry
Streams.write(toJsonObject(element), writer);
} else throw e;
} Prevention
- Do not subclass JsonElement; compose instead.
- If subclassing is unavoidable, register a custom TypeAdapter for it.
- Convert custom elements to JsonObject before serialization.
- Add a unit test that serializes every JsonElement variant you use.
When it happens
Trigger: Calling gson.toJson(element) or Streams.write where element is a user-defined subclass of JsonElement that does not behave as one of the four standard subtypes, or where the subclass overrides isJsonNull etc. to return false.
Common situations: Extending JsonElement/JsonObject to add domain methods and serializing instances directly; library code that wraps JsonElement in a proxy subclass.
Related errors
- Custom JsonElement subclass
- Accessor threw exception
- Already wrote a name, expecting a value.
- Attempted to serialize java.lang.Class
- cannot serialize because it already defines a field named
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/2ea62b287f2916ab.
Report an issue: GitHub.
Appendix: source
Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonElementTypeAdapter.java:168
}
} else if (value.isJsonArray()) {
out.beginArray();
for (JsonElement e : value.getAsJsonArray()) {
write(out, e);
}
out.endArray();
} else if (value.isJsonObject()) {
out.beginObject();
for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
out.name(e.getKey());
write(out, e.getValue());
}
out.endObject();
} else {
throw new IllegalArgumentException("Couldn't write " + value.getClass());
}
}
}
View on GitHub (pinned to 310ac341f2)