google/gson · error · IllegalStateException

Expected one JSON element but was {stack}

Error message

Expected one JSON element but was {stack}

What it means

Thrown by JsonTreeWriter.get() when the internal stack is non-empty, meaning there are still-open JSON arrays or objects that were begun with beginArray()/beginObject() but never closed. The writer cannot return a single root JsonElement because the document is structurally incomplete. The exception message lists the leftover stack to aid debugging.

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java:71

  private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");

  /** The JsonElements and JsonArrays under modification, outermost to innermost. */
  private final List<JsonElement> stack = new ArrayList<>();

  /** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */
  private String pendingName;

  /** the JSON element constructed by this writer. */
  private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;

  public JsonTreeWriter() {
    super(UNWRITABLE_WRITER);
  }

  /** Returns the top level object produced by this writer. */
  public JsonElement get() {
    if (!stack.isEmpty()) {
      throw new IllegalStateException("Expected one JSON element but was " + stack);
    }
    return product;
  }

  private JsonElement peek() {
    return stack.get(stack.size() - 1);
  }

  private void put(JsonElement value) {
    if (pendingName != null) {
      if (!value.isJsonNull() || getSerializeNulls()) {
        JsonObject object = (JsonObject) peek();
        object.add(pendingName, value);
      }
      pendingName = null;
    } else if (stack.isEmpty()) {
      product = value;
    } else {

View on GitHub (pinned to 8b8628c656)

Solutions

  1. Match every beginObject()/beginArray() with a corresponding endObject()/endArray() in a try/finally so exceptions can't leave the stack open.
  2. Before calling get(), assert the writer has no open containers; if unsure, track depth yourself.
  3. Restructure the serializer to write its root element with a single value(...) call if it only emits one scalar, avoiding begin/end entirely.
  4. Use try-with-resources style discipline: pair begin/end in the same lexical scope.

Example fix

// before: beginObject() with no matching endObject()
JsonTreeWriter w = new JsonTreeWriter();
w.beginObject().name("a").value(1);
w.get(); // throws: stack not empty

// after: always close what you open
JsonTreeWriter w = new JsonTreeWriter();
w.beginObject();
try {
  w.name("a").value(1);
} finally {
  w.endObject();
}
w.get();
Defensive patterns

Strategy: validation

Validate before calling

// Ensure all containers are closed before calling get()
// Track depth externally and assert zero before get()
if (openDepth != 0) throw new IllegalStateException(openDepth + " unclosed containers");
JsonElement root = treeWriter.get();

Try / catch

try {
  return treeWriter.get();
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Expected one JSON element")) {
    // attempt to close open containers, or fail with context
    throw new IllegalStateException("Document incomplete: unclosed containers remain", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling JsonTreeWriter.get() (or equivalently letting a TypeAdapter that writes to a JsonTreeWriter finish) without first calling endObject()/endArray() for every beginObject()/beginArray(). Imbalance of begin/end calls is the only trigger.

Common situations: Custom serializer (TypeAdapter.write or JsonSerializer) that calls beginObject() but returns early on an exception path without closing; forgetting to match every beginArray() with endArray(); adapter that conditionally opens a structure then skips the close on a branch; tests that build a tree manually and forget to close.

Related errors


AI-assisted analysis of google/gson@8b8628c656 (2026-08-04). Data as JSON: /data/errors/6e350cf401c10374.json. Report an issue: GitHub.