Why Does Fastjson Occasionally Throw ‘Can Not Cast to String’? A Deep Dive into a Ghost Bug
This article chronicles a puzzling Fastjson bug that manifested intermittently in production, detailing the investigation steps, code examples, and the eventual discovery that an early Fastjson version mishandled generic type caching, which was fixed by upgrading the library.
Preface
After many years of development I have encountered countless bugs, but this one was especially elusive. The error appeared intermittently in the test environment and vanished when debugging, making it seem like a “Schrödinger bug”.
Cause
A colleague submitted a feature that triggered a Fastjson JSONException: cannot cast to String. The JSON data was correct, but the exception indicated a type mismatch during deserialization.
com.alibaba.fastjson.JSONException: can not cast to String, value : {"code":"00","msg":"成功","data":{...}}The stack trace showed the error originating from Fastjson’s type conversion utilities.
Initial Investigation
Running the code locally with the same JSON and the same generic method
public <T> T executeLua(String luaName, Class<T> c, Object... args)parsed the data without issue, even under multithreaded loops. The problem only reproduced on the server.
Debugging in the test environment sometimes made the error disappear, suggesting the bug was tied to observation.
First Debugging Attempt
By placing breakpoints around the JSON parsing line T result = JSON.parseObject(json, c);, the error could be captured after dozens of executions, confirming that the bug was not a one‑off.
Second Investigation – Generic Types
Adding an explicit generic type with TypeReference eliminated the error, indicating that Fastjson’s handling of raw generic types was involved. Research revealed that early Fastjson versions cached the generic type of the previous deserialization and reused it for subsequent calls.
LuaResult<Map<String,Object>> result = executeLua("xxxx", new TypeReference<LuaResult<Map<String,Object>>>(){}, args);Reproducing the Bug
A minimal program was written to reproduce the issue using Fastjson 1.2.29. The first two deserialization calls succeeded, but the third call (without an explicit generic) threw the same exception, confirming the caching behavior.
public static void main(String... args) throws Exception {
try {
String json = "{\"code\":\"00\",\"msg\":\"成功\",\"data\":{\"xxx\":21,\"yyy\":5}}";
LuaResult result = JSON.parseObject(json, LuaResult.class);
System.out.println(result);
} catch (Exception e) { e.printStackTrace(); }
try {
String json1 = "{\"msg\":\"成功\",\"data\":\"31\",\"code\":\"00\"}";
LuaResult<Integer> result = JSON.parseObject(json1, new TypeReference<LuaResult<Integer>>(){});
System.out.println(result);
} catch (Exception e) { e.printStackTrace(); }
// The third call reproduces the bug
try {
String json = "{\"code\":\"00\",\"msg\":\"成功\",\"data\":{\"xxx\":21,\"yyy\":5}}";
LuaResult result = JSON.parseObject(json, LuaResult.class);
System.out.println(result);
} catch (Exception e) { e.printStackTrace(); }
}Resolution
Testing successive Fastjson versions showed that the bug disappeared starting from 1.2.33, where the author fixed the issue related to generic type caching without explicit parameters.
Conclusion
The “Schrödinger bug” was caused by Fastjson’s early‑version handling of generic type caching. When a previous deserialization used a concrete generic (e.g., Integer), subsequent raw‑type deserializations incorrectly reused that generic, leading to can not cast to int errors. Upgrading Fastjson to 1.2.33 or later resolves the issue.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sanyou's Java Diary
Passionate about technology, though not great at solving problems; eager to share, never tire of learning!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
