google/gson · error · IllegalStateException
Did not expect a name
Error message
Did not expect a name
What it means
Thrown by JsonTreeWriter.name() when the stack is empty (no container is open) or when pendingName is already non-null (a previous name() has not yet been consumed by a value). JSON object members must alternate name then value; a second name without an intervening value, or a name with no enclosing object, is illegal.
Source
Thrown at gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java:150
@Override
public JsonWriter endObject() throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonObject) {
stack.remove(stack.size() - 1);
return this;
}
throw new IllegalStateException();
}
@CanIgnoreReturnValue
@Override
public JsonWriter name(String name) throws IOException {
Objects.requireNonNull(name, "name == null");
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException("Did not expect a name");
}
JsonElement element = peek();
if (element instanceof JsonObject) {
pendingName = name;
return this;
}
throw new IllegalStateException("Please begin an object before writing a name.");
}
@CanIgnoreReturnValue
@Override
public JsonWriter value(String value) throws IOException {
if (value == null) {
return nullValue();
}
put(new JsonPrimitive(value));
return this;
}View on GitHub (pinned to 8b8628c656)
Solutions
- Ensure exactly one value (or container) follows every name() before the next name().
- Wrap name()+value() in a helper that always pairs them, so a skip can't leave a dangling name.
- Only call name() between beginObject() and endObject(), never at top level.
- Add a test exercising conditional fields to catch the dangling-name path.
Example fix
// before: two name() calls in a row
out.beginObject();
out.name("a");
out.name("b").value(2); // throws: Did not expect a name
out.endObject();
// after: pair each name with a value
out.beginObject();
out.name("a").value(1);
out.name("b").value(2);
out.endObject(); Defensive patterns
Strategy: validation
Validate before calling
// Only call name() inside an open object, and only when no name is pending
if (stackEmpty || pendingName != null) throw new IllegalStateException("name() not allowed here");
out.name(k); Try / catch
try {
out.name(k);
} catch (IllegalStateException e) {
if ("Did not expect a name".equals(e.getMessage())) {
// likely: double name() or name() outside object; write a null value to recover
out.nullValue();
out.name(k);
} else throw e;
} Prevention
- Always follow name() with exactly one value/container before the next name().
- Wrap name()+value() in a helper that guarantees pairing.
- Call name() only between beginObject() and endObject().
When it happens
Trigger: Calling name() twice in a row inside an object without writing a value between them; calling name() before any beginObject(); calling name() on a writer that just consumed a value and is back to expecting a name but pendingName is somehow still set (double-name bug).
Common situations: Serializer loop that emits name() then conditionally skips the value but still advances; copy-paste leaving two name() calls adjacent; serializer that writes name() at the top level instead of inside beginObject()/endObject().
Related errors
- Expected one JSON element but was {stack}
- Please begin an object before writing a name.
- Incomplete document
- Unexpected {peeked} when reading a JsonElement.
- String contains non-ASCII characters: {s}{location}
AI-assisted analysis of google/gson@8b8628c656 (2026-08-04).
Data as JSON: /data/errors/239e9240286bbd15.json.
Report an issue: GitHub.