google/gson · error · IllegalStateException

Dangling name: {}

Error message

Dangling name: {}

What it means

Thrown by JsonWriter.closeScope() (JsonWriter.java:460-462) when deferredName is non-null at the moment of closing a scope. A non-null deferredName means name() was called but no value was written yet; closing the object/array here would emit a name with no value, producing invalid JSON, so the writer rejects it.

Source

Thrown at gson/src/main/java/com/google/gson/stream/JsonWriter.java:461

  /** Enters a new scope by appending any necessary whitespace and the given bracket. */
  @CanIgnoreReturnValue
  private JsonWriter openScope(int empty, char openBracket) throws IOException {
    beforeValue();
    push(empty);
    out.write(openBracket);
    return this;
  }

  /** Closes the current scope by appending any necessary whitespace and the given bracket. */
  @CanIgnoreReturnValue
  private JsonWriter closeScope(int empty, int nonempty, char closeBracket) throws IOException {
    int context = peek();
    if (context != nonempty && context != empty) {
      throw new IllegalStateException("Nesting problem.");
    }
    if (deferredName != null) {
      throw new IllegalStateException("Dangling name: " + deferredName);
    }

    stackSize--;
    if (context == nonempty) {
      newline();
    }
    out.write(closeBracket);
    return this;
  }

  private void push(int newTop) {
    if (stackSize == stack.length) {
      stack = Arrays.copyOf(stack, stackSize * 2);
    }
    stack[stackSize++] = newTop;
  }

  /** Returns the value on the top of the stack. */

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Always write a value (or nullValue()) after name(); if the value should be omitted, do not call name() at all.
  2. Decide whether to serialize a field before calling name().
  3. If you started a name you cannot complete, emit nullValue() to keep the JSON valid, or restructure to avoid the dangling name.
  4. Set serializeNulls consistently if you rely on nullValue() to fill the slot.

Example fix

// before
writer.beginObject();
writer.name("opt");
if (hasOpt) writer.value(v);
writer.endObject(); // dangling name when !hasOpt

// after
writer.beginObject();
if (hasOpt) {
  writer.name("opt").value(v);
}
writer.endObject();
Defensive patterns

Strategy: validation

Validate before calling

writer.beginObject();
if (shouldWriteOpt) {
  writer.name("opt").value(v);
}
writer.endObject();

Type guard

// Decide before calling name() whether a value will follow.
boolean willEmitValue = (v != null && shouldSerialize);
if (willEmitValue) writer.name("opt").value(v);

Try / catch

try {
  writer.endObject();
} catch (IllegalStateException e) {
  if (e.getMessage().contains("Dangling name")) {
    writer.nullValue(); // make JSON valid, then close
    writer.endObject();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling name("x") and then immediately endObject() or endArray() without an intervening value/nullValue call. Also conditional serializers that emit a name and then skip the value on a branch.

Common situations: Conditional field serialization that writes the name before checking whether to skip the value; serializers that emit a name then hit an exception path that closes the object; refactors that move the value write behind a flag.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/4d98415481e404f8. Report an issue: GitHub.