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

  1. Avoid subclassing JsonElement; favor composition over inheritance.
  2. Register a custom TypeAdapter for your JsonElement subclass via GsonBuilder.registerTypeAdapter.
  3. Convert the custom element to a standard JsonObject before serializing.
  4. 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

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


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)