alibaba/spring-ai-alibaba · error · IllegalStateException
Content Type used for store state '%s' is different from one
Error message
Content Type used for store state '%s' is different from one '%s' used for deserialize it
What it means
H2Saver stores serialized checkpoint state with a content type marker. decodeState compares the stored contentType with the current stateSerializer.contentType(); a mismatch means the persisted rows were written with a different serializer configuration than the one used to read them, and deserialization is refused with IllegalStateException.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/h2/H2Saver.java:289
}
}
private void executeStatements(Statement statement, String sqlStatements) throws SQLException {
for (String sql : sqlStatements.split(";")) {
if (!sql.isBlank()) {
statement.execute(sql);
}
}
}
private String encodeState(Map<String, Object> data) throws IOException {
return Base64.getEncoder().encodeToString(stateSerializer.dataToBytes(data));
}
private Map<String, Object> decodeState(String binaryPayload, String contentType)
throws IOException, ClassNotFoundException {
if (!Objects.equals(contentType, stateSerializer.contentType())) {
throw new IllegalStateException(
format("Content Type used for store state '%s' is different from one '%s' used for deserialize it",
contentType,
stateSerializer.contentType()));
}
return stateSerializer.dataFromBytes(Base64.getDecoder().decode(binaryPayload));
}
private Checkpoint readCheckpoint(ResultSet resultSet)
throws SQLException, IOException, ClassNotFoundException {
return Checkpoint.builder()
.id(resultSet.getString(1))
.nodeId(resultSet.getString(2))
.nextNodeId(resultSet.getString(3))
.state(decodeState(resultSet.getString(4), resultSet.getString(5)))
.build();
}
@OverrideView on GitHub (pinned to f82da0b50f)
Solutions
- Reconfigure the H2Saver to use the same stateSerializer/contentType as when the checkpoints were written
- Clear or migrate the old checkpoint data and re-run to regenerate checkpoints
- Write a migration that deserializes with the old serializer and re-persists with the new one
- Use a fresh H2 database file/table when changing serializer
Example fix
// before H2Saver.builder().stateSerializer(new ObjectStateSerializer()).build(); // data written as JSON // after H2Saver.builder().stateSerializer(new JacksonStateSerializer()).build(); // matches stored contentType
Defensive patterns
Strategy: try-catch
Validate before calling
// before reading: check stored contentType column matches serializer.contentType()
Type guard
boolean serializerMatches(String storedContentType, StateSerializer s) { return Objects.equals(storedContentType, s.contentType()); } Try / catch
try { checkpoint = saver.get(threadId); } catch (IllegalStateException e) { /* serializer mismatch: migrate or recreate checkpoints */ } Prevention
- Keep serializer configuration stable across deployments
- Include contentType in schema migrations
- Use a fresh checkpoint DB when changing serializers
When it happens
Trigger: readCheckpoint() on rows previously written by an H2Saver configured with a different state serializer (different contentType), e.g. after switching from JSON to Java serialization or a custom serializer.
Common situations: Upgrading the library or changing serializer config while keeping an old H2 checkpoint database; sharing one checkpoint table across apps with different serializers.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Unable to load checkpoint
- Content Type used for store state '%s' is different from one
- Unable to load checkpoints
- Unable to load latest checkpoint
- Unable to insert checkpoint
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/766dedc9810df4a7.
Report an issue: GitHub.