alibaba/spring-ai-alibaba · error · RuntimeException

Failed to serialize/deserialize checkpoints

Error message

Failed to serialize/deserialize checkpoints

What it means

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.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java:362

			}
			else {
				// Add Checkpoint
				checkpoints.push(checkpoint);
			}

			RBucket<byte[]> bucket = redisson.getBucket(contentKey, ByteArrayCodec.INSTANCE);
			bucket.set(serializeCheckpoints(checkpoints));
			if (ttl > 0) {
				bucket.expire(java.time.Duration.ofMillis(ttlUnit.toMillis(ttl)));
			}
			return RunnableConfig.builder(config).checkPointId(checkpoint.getId()).build();

		}
		catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		catch (IOException | ClassNotFoundException e) {
			throw new RuntimeException("Failed to serialize/deserialize checkpoints", e);
		}
		finally {
			if (lock.isHeldByCurrentThread()) {
				lock.unlock();
			}
		}
	}

	@Override
	public Tag release(RunnableConfig config) throws Exception {
		Optional<String> threadNameOpt = config.threadId();
		if (!threadNameOpt.isPresent()) {
			throw new IllegalArgumentException("threadId is not allow null");
		}

		String threadName = threadNameOpt.get();
		RLock lock = redisson.getLock(LOCK_PREFIX + threadName);
		boolean tryLock = false;

View on GitHub (pinned to f82da0b50f)

Solutions

  1. 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
  2. Make all state values serializable with the chosen serializer (implement Serializable, add Jackson annotations/mixins for complex types)
  3. Pin the same StateSerializer across all writers and upgrades; migrate data explicitly when changing serializers
  4. Test put/get round-trip with your real state before deploying

Example fix

// before
state.put("conn", someNonSerializableConnection);
// after
state.put("connConfig", connectionConfigToString(someNonSerializableConnection));
Defensive patterns

Strategy: validation

Validate before calling

// verify all state values are serializable before put
for (var e : state.value().entrySet()) {
    if (!(e.getValue() instanceof Serializable) && jacksonMode) { /* add mixin or convert */ }
}

Type guard

boolean isSerializableState(Object v) { return v == null || v instanceof Serializable || v instanceof String || v instanceof Number || v instanceof Boolean; }

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/6afc58ae186fea1b. Report an issue: GitHub.