{"record":{"id":"b7595a730ba4f30b","repo":"google/gson","slug":"expected-one-json-element-but-was-stack","errorCode":null,"errorMessage":"Expected one JSON element but was ${stack}","messagePattern":"Expected one JSON element but was (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java","lineNumber":71,"sourceCode":"  private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive(\"closed\");\n\n  /** The JsonElements and JsonArrays under modification, outermost to innermost. */\n  private final List<JsonElement> stack = new ArrayList<>();\n\n  /** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */\n  private String pendingName;\n\n  /** the JSON element constructed by this writer. */\n  private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;\n\n  public JsonTreeWriter() {\n    super(UNWRITABLE_WRITER);\n  }\n\n  /** Returns the top level object produced by this writer. */\n  public JsonElement get() {\n    if (!stack.isEmpty()) {\n      throw new IllegalStateException(\"Expected one JSON element but was \" + stack);\n    }\n    return product;\n  }\n\n  private JsonElement peek() {\n    return stack.get(stack.size() - 1);\n  }\n\n  private void put(JsonElement value) {\n    if (pendingName != null) {\n      if (!value.isJsonNull() || getSerializeNulls()) {\n        JsonObject object = (JsonObject) peek();\n        object.add(pendingName, value);\n      }\n      pendingName = null;\n    } else if (stack.isEmpty()) {\n      product = value;\n    } else {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java#L53-L89","documentation":"JsonTreeWriter builds a JsonElement tree and get() returns the finished product only when its internal container stack is empty - i.e. every beginObject()/beginArray() has been matched by an endObject()/endArray(). If the stack is non-empty when get() is called, an open container remains unclosed and the document is structurally incomplete, so Gson throws IllegalStateException. This is typically reached via TypeAdapter.toJsonTree() or when a custom adapter writes to a JsonTreeWriter.","triggerScenarios":"Calling writer.get() after beginObject()/beginArray() without the matching end call; a custom TypeAdapter.write() that opens containers but returns (or throws) before closing them; conditional code that begins a container on one branch but forgets to end it.","commonSituations":"Custom TypeAdapter with an early return or exception that skips endObject(); copy-paste bug omitting the closing call; logic that opens nested containers only conditionally.","solutions":["Ensure every beginObject() has a matching endObject() and every beginArray() a matching endArray() before calling get()","Wrap begin/end in try-finally so containers close even on an exception path","Prefer Gson.toJsonTree() over manual JsonTreeWriter use so lifecycle is managed internally"],"exampleFix":"// before - stack not empty, get() throws\nwriter.beginObject();\nwriter.name(\"a\").value(1);\nreturn writer.get();\n\n// after - close the container first\nwriter.beginObject();\nwriter.name(\"a\").value(1);\nwriter.endObject();\nreturn writer.get();","handlingStrategy":"validation","validationCode":"// Track open-container depth and assert it is zero before get()\nint depth = openContainers; // your counter\nif (depth != 0) {\n    throw new IllegalStateException(\"Cannot get(): \" + depth + \" open containers\");\n}\nJsonElement result = writer.get();","typeGuard":null,"tryCatchPattern":"try {\n  JsonElement el = writer.get();\n} catch (IllegalStateException e) {\n  // 'Expected one JSON element but was [...]': an open container remains;\n  // close it or discard the writer and rebuild the tree\n}","preventionTips":["Always pair beginObject/beginArray with matching end calls","Use try-finally to guarantee end calls run on exception paths","Prefer Gson.toJsonTree() which manages JsonTreeWriter lifecycle internally"],"tags":["json","serialization","tree-model","state-machine"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}