SkinUploadSocket.onClose assumes the WebSocket close reason is always a JsonObject.
That assumption is not always valid. In some cases the close reason is a JSON primitive or plain string, but the code still attempts to deserialize it directly as JsonObject, which causes Gson to throw.
Exception observed:
com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $
Stack trace:
[21:07:33] [WebSocketConnectReadThread-92/ERROR]: [floodgate] [debug] Got an error
com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $
at com.google.gson.internal.bind.TypeAdapters$32$1.read(TypeAdapters.java:923)
at com.google.gson.Gson.fromJson(Gson.java:1358)
at com.google.gson.Gson.fromJson(Gson.java:1259)
at com.google.gson.Gson.fromJson(Gson.java:1169)
at com.google.gson.Gson.fromJson(Gson.java:1106)
at org.geysermc.floodgate.skin.SkinUploadSocket.onClose(SkinUploadSocket.java:155)
at org.java_websocket.client.WebSocketClient.onWebsocketClose(WebSocketClient.java:688)
at org.java_websocket.WebSocketImpl.closeConnection(WebSocketImpl.java:557)
at org.java_websocket.WebSocketImpl.eot(WebSocketImpl.java:612)
at org.java_websocket.client.WebSocketClient.run(WebSocketClient.java:546)
at java.base/java.lang.Thread.run(Thread.java:1474)
The problem is the unconditional deserialization as JsonObject. If the close reason is not an object (for example a primitive JSON value), Gson throws and the close handler fails.
The close reason format is not guaranteed to always be a JSON object, so this path should handle it defensively.
A safer approach would be:
- parse the payload as
JsonElement
- check
isJsonObject() before accessing object fields
- otherwise ignore or log the raw reason
Environment
- Floodgate:
Build #130
- Geyser:
Build #1093
- Server software:
Spigot
- Server version:
1.21.11 (4596)
- Java:
25
SkinUploadSocket.onCloseassumes the WebSocket close reason is always aJsonObject.That assumption is not always valid. In some cases the close reason is a JSON primitive or plain string, but the code still attempts to deserialize it directly as
JsonObject, which causes Gson to throw.Exception observed:
Stack trace:
The problem is the unconditional deserialization as
JsonObject. If the close reason is not an object (for example a primitive JSON value), Gson throws and the close handler fails.The close reason format is not guaranteed to always be a JSON object, so this path should handle it defensively.
A safer approach would be:
JsonElementisJsonObject()before accessing object fieldsEnvironment
Build #130Build #1093Spigot1.21.11 (4596)25