{"record":{"id":"1a710e0dd139dc47","repo":"google/gson","slug":"deserialization-is-unsupported","errorCode":null,"errorMessage":"Deserialization is unsupported","messagePattern":"Deserialization is unsupported","errorType":"exception","errorClass":"InvalidObjectException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java","lineNumber":91,"sourceCode":"  }\n\n  @Override\n  public String toString() {\n    return value;\n  }\n\n  /**\n   * If somebody is unlucky enough to have to serialize one of these, serialize it as a BigDecimal\n   * so that they won't need Gson on the other side to deserialize it.\n   */\n  private Object writeReplace() {\n    return asBigDecimal();\n  }\n\n  private void readObject(ObjectInputStream in) throws IOException {\n    // Don't permit directly deserializing this class; writeReplace() should have written a\n    // replacement\n    throw new InvalidObjectException(\"Deserialization is unsupported\");\n  }\n\n  /**\n   * Compares this LazilyParsedNumber with the specified LazilyParsedNumber. The comparison is\n   * lexicographical, based on the string values of the two numbers, so it does not in general\n   * correspond to numeric comparison. For numeric comparison, call {@link #asBigDecimal()} on both\n   * numbers and compare the results.\n   */\n  @Override\n  public int compareTo(LazilyParsedNumber other) {\n    return value.compareTo(other.value);\n  }\n\n  @Override\n  public int hashCode() {\n    return value.hashCode();\n  }\n","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java#L73-L109","documentation":"LazilyParsedNumber is a Gson-internal number holder that is never meant to be reconstructed via Java serialization. Its writeReplace() emits a BigDecimal so other JVMs need Gson on neither side; its readObject() throws InvalidObjectException to prevent creating an instance in an inconsistent state. Triggered only by circumventing writeReplace, e.g. via a malicious or buggy custom ObjectInputStream.","triggerScenarios":"An ObjectInputStream directly deserializing a stream whose class descriptor names LazilyParsedNumber (bypassing or overriding writeReplace), reaching LazilyParsedNumber.readObject at LazilyParsedNumber.java:88.","commonSituations":"Custom deserialization harnesses; malformed/manipulated serialized streams; tests that mock serialization of Gson's internal types; transferring Gson-parsed numbers across an RMI or ObjectOutputStream boundary and then attempting to read them back without the writeReplace replacement.","solutions":["Do not serialize LazilyParsedNumber directly; let writeReplace emit a BigDecimal and deserialize that instead.","If you must move parsed numbers across serialization, convert to BigDecimal/Long/String before writing: new BigDecimal(lpn.toString()).","Re-examine any custom ObjectInputStream subclass that overrides resolveClass/readClassDescriptor and may be selecting the original class over its replacement."],"exampleFix":"// before\noos.writeObject(gsonParsedNumber); // later fails if stream is tampered\n// after\noos.writeObject(new BigDecimal(gsonParsedNumber.toString()));","handlingStrategy":"validation","validationCode":"// never attempt to deserialize LazilyParsedNumber directly\nObject toSerialize = (n instanceof LazilyParsedNumber) ? new BigDecimal(n.toString()) : n;","typeGuard":"static Object safeSerialize(Number n) {\n  return (n instanceof com.google.gson.internal.LazilyParsedNumber) ? new java.math.BigDecimal(n.toString()) : n;\n}","tryCatchPattern":"try (ObjectInputStream ois = new ObjectInputStream(in)) {\n  Object o = ois.readObject();\n} catch (InvalidObjectException e) {\n  // stream bypassed writeReplace; re-read expecting BigDecimal\n}","preventionTips":["Do not persist Gson-internal Number types via Java serialization; convert to BigDecimal first.","Treat LazilyParsedNumber as an internal type: never depend on its serialized form.","Prefer JSON or explicit BigDecimal/Long for cross-process number transfer."],"tags":["gson","serialization","java-io"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}