{"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/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java#L73-L109","documentation":"Thrown by LazilyParsedNumber.readObject(ObjectInputStream) as InvalidObjectException. LazilyParsedNumber is an internal Gson type that stores a JSON number as a string and converts lazily. Its writeReplace() serializes it as a BigDecimal, so direct Java deserialization back into a LazilyParsedNumber is intentionally blocked to keep the serialized form portable and dependency-free.","triggerScenarios":"Calling ObjectInputStream.readObject() on a stream that was serialized from a LazilyParsedNumber without going through its writeReplace (e.g. a tampered or legacy stream, or reflection-based deserialization that bypasses the replacement). Normal serialization writes a BigDecimal and reads a BigDecimal, so this only fires when the stream claims to be a LazilyParsedNumber directly.","commonSituations":"Deserializing a legacy object graph that was serialized with an old or buggy Gson version that did not install writeReplace; tampered/forged streams; custom ObjectInputStream subclasses that resolve classes manually; debugging tools that read raw object streams.","solutions":["Do not serialize LazilyParsedNumber directly; serialize the value as a BigDecimal or a String and reconstruct via new LazilyParsedNumber(string).","If reading a stored stream, reserialize the source data through Gson (toJson/fromJson) instead of Java serialization.","Upgrade Gson; ensure writeReplace is active by not overriding it in subclasses."],"exampleFix":"// before: Java-serializing an internal Gson number\nObjectOutputStream out = ...\nout.writeObject(gsonNumber);  // LazilyParsedNumber\nObjectInputStream in = ...\nNumber n = (Number) in.readObject(); // InvalidObjectException\n\n// after: serialize as BigDecimal/JSON\nout.writeObject(gsonNumber instanceof LazilyParsedNumber ? new BigDecimal(gsonNumber.toString()) : gsonNumber);","handlingStrategy":"try-catch","validationCode":"// Before serializing a Number from Gson, convert to a portable form\nstatic Number portable(Number n) {\n  return (n instanceof LazilyParsedNumber) ? new BigDecimal(n.toString()) : n;\n}\n// usage: out.writeObject(portable(gsonNumber));","typeGuard":"static boolean isPortableNumber(Number n) {\n  return !(n.getClass().getName().contains(\"LazilyParsedNumber\"));\n}","tryCatchPattern":"try {\n  Object o = in.readObject();\n} catch (InvalidObjectException e) {\n  if (e.getMessage().contains(\"Deserialization is unsupported\")) {\n    // reserialize the source via JSON instead of Java serialization\n    throw new IllegalStateException(\"Use Gson JSON transport, not Java serialization\", e);\n  } else throw e;\n}","preventionTips":["Never Java-serialize internal Gson types; use toJson/fromJson.","Convert LazilyParsedNumber to BigDecimal before persisting.","Keep serialization confined to plain JDK collections and primitives."],"tags":["gson","serialization","internal","number","invalidobjectexception"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}