{"id":"ee70cdd6f1a68abe","repo":"google/gson","slug":"dangling-name-deferredname","errorCode":null,"errorMessage":"Dangling name: \" + deferredName","messagePattern":"Dangling name: \" \\+ deferredName","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/stream/JsonWriter.java","lineNumber":461,"sourceCode":"\n  /** Enters a new scope by appending any necessary whitespace and the given bracket. */\n  @CanIgnoreReturnValue\n  private JsonWriter openScope(int empty, char openBracket) throws IOException {\n    beforeValue();\n    push(empty);\n    out.write(openBracket);\n    return this;\n  }\n\n  /** Closes the current scope by appending any necessary whitespace and the given bracket. */\n  @CanIgnoreReturnValue\n  private JsonWriter closeScope(int empty, int nonempty, char closeBracket) throws IOException {\n    int context = peek();\n    if (context != nonempty && context != empty) {\n      throw new IllegalStateException(\"Nesting problem.\");\n    }\n    if (deferredName != null) {\n      throw new IllegalStateException(\"Dangling name: \" + deferredName);\n    }\n\n    stackSize--;\n    if (context == nonempty) {\n      newline();\n    }\n    out.write(closeBracket);\n    return this;\n  }\n\n  private void push(int newTop) {\n    if (stackSize == stack.length) {\n      stack = Arrays.copyOf(stack, stackSize * 2);\n    }\n    stack[stackSize++] = newTop;\n  }\n\n  /** Returns the value on the top of the stack. */","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/stream/JsonWriter.java#L443-L479","documentation":"Thrown by JsonWriter.closeScope() when there is a deferred name still pending while trying to close an object or array. A name() call must always be followed by exactly one value (or nested structure) before the scope can be closed; closing with a dangling name would produce invalid JSON.","triggerScenarios":"Calling writer.name(\"x\") followed directly by endObject() without writing a value, or any code path where name() is called but the subsequent value write is skipped (e.g. behind an if that evaluates false).","commonSituations":"Conditional field serialization that writes the name unconditionally but skips the value; early returns/exceptions between name() and value(); refactoring that moves name() out of an if but leaves value() inside.","solutions":["Ensure every name() is immediately followed by a value/nullValue/begin* call on all paths.","Decide whether to emit a field (including its name) before calling name(); guard the whole name+value pair together.","If a value may be absent, call nullValue() (when serializeNulls is on) or omit both name and value.","Restructure conditional serialization so the name is only written once the value is determined."],"exampleFix":"// before\nwriter.name(\"optional\");\nif (value != null) {\n  writer.value(value);\n}\nwriter.endObject(); // throws if value was null: dangling name\n\n// after\nif (value != null) {\n  writer.name(\"optional\").value(value);\n}\nwriter.endObject();","handlingStrategy":"validation","validationCode":"// Only write name when you can also write the value\nif (value != null) {\n  writer.name(\"field\").value(value);\n}\n// never: writer.name(\"field\") then conditionally skip value","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write name and value together; never split them across branches.","Guard the entire name+value pair, not just the value, behind the condition.","Call nullValue() explicitly if you want to emit null for a field.","Unit-test conditional serialization paths to ensure no dangling names."],"tags":["gson","jsonwriter","state-machine","conditional-serialization"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}