{"record":{"id":"6afc58ae186fea1b","repo":"alibaba/spring-ai-alibaba","slug":"failed-to-serialize-deserialize-checkpoints","errorCode":null,"errorMessage":"Failed to serialize/deserialize checkpoints","messagePattern":"Failed to serialize/deserialize checkpoints","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java","lineNumber":362,"sourceCode":"\t\t\t}\n\t\t\telse {\n\t\t\t\t// Add Checkpoint\n\t\t\t\tcheckpoints.push(checkpoint);\n\t\t\t}\n\n\t\t\tRBucket<byte[]> bucket = redisson.getBucket(contentKey, ByteArrayCodec.INSTANCE);\n\t\t\tbucket.set(serializeCheckpoints(checkpoints));\n\t\t\tif (ttl > 0) {\n\t\t\t\tbucket.expire(java.time.Duration.ofMillis(ttlUnit.toMillis(ttl)));\n\t\t\t}\n\t\t\treturn RunnableConfig.builder(config).checkPointId(checkpoint.getId()).build();\n\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tcatch (IOException | ClassNotFoundException e) {\n\t\t\tthrow new RuntimeException(\"Failed to serialize/deserialize checkpoints\", e);\n\t\t}\n\t\tfinally {\n\t\t\tif (lock.isHeldByCurrentThread()) {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic Tag release(RunnableConfig config) throws Exception {\n\t\tOptional<String> threadNameOpt = config.threadId();\n\t\tif (!threadNameOpt.isPresent()) {\n\t\t\tthrow new IllegalArgumentException(\"threadId is not allow null\");\n\t\t}\n\n\t\tString threadName = threadNameOpt.get();\n\t\tRLock lock = redisson.getLock(LOCK_PREFIX + threadName);\n\t\tboolean tryLock = false;","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java#L344-L380","documentation":"RedisSaver.put() wraps IOException or ClassNotFoundException from serializing the new checkpoint list (or deserializing the existing one) into RuntimeException('Failed to serialize/deserialize checkpoints'). The write to CHECKPOINT_PREFIX+threadId failed because the Checkpoint/state objects cannot be converted to bytes, or the existing bytes cannot be read back, so nothing was persisted.","triggerScenarios":"State values are not serializable by the configured StateSerializer (e.g. non-serializable POJOs with Java serialization, Jackson-failing types like un-annotated complex objects); existing Redis blob written by an incompatible serializer; classes renamed/removed causing ClassNotFoundException when reading existing checkpoints.","commonSituations":"Storing lambdas, streams, or non-serializable client objects in agent state; switching JacksonStateSerializer vs default serializer on existing data; library upgrade changing Checkpoint's serialized shape.","solutions":["Check the cause: ClassNotFoundException/InvalidClassException means stale or incompatible existing data — delete the affected CHECKPOINT_PREFIX keys or keep the original classes on the classpath","Make all state values serializable with the chosen serializer (implement Serializable, add Jackson annotations/mixins for complex types)","Pin the same StateSerializer across all writers and upgrades; migrate data explicitly when changing serializers","Test put/get round-trip with your real state before deploying"],"exampleFix":"// before\nstate.put(\"conn\", someNonSerializableConnection);\n// after\nstate.put(\"connConfig\", connectionConfigToString(someNonSerializableConnection));","handlingStrategy":"validation","validationCode":"// verify all state values are serializable before put\nfor (var e : state.value().entrySet()) {\n    if (!(e.getValue() instanceof Serializable) && jacksonMode) { /* add mixin or convert */ }\n}","typeGuard":"boolean isSerializableState(Object v) { return v == null || v instanceof Serializable || v instanceof String || v instanceof Number || v instanceof Boolean; }","tryCatchPattern":"try { saver.put(config, cp); } catch (RuntimeException e) { if (e.getMessage() != null && e.getMessage().contains(\"Failed to serialize\")) { log.error(\"Unserializable state or incompatible stored data\", e.getCause()); throw new IllegalStateException(\"Fix state serialization before persisting\", e); } throw e; }","preventionTips":["Keep only serializable values in agent state","Pin one StateSerializer across all instances and upgrades","Add a put/get round-trip integration test with real state"],"tags":["redis","serialization","checkpoint","serializer"],"backgroundTag":"json-marshal-failed","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}