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
OracleSaver stores each checkpoint state along with the content type produced by the state serializer. When reading a checkpoint back, decodeState compares the content type stored in the database row with the content type the currently configured serializer produces. If they differ, the stored binary payload may be incompatible with the current deserializer, so the saver refuses to proceed with an IllegalStateException rather than return corrupt state.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/oracle/OracleSaver.java:337
var base64Data = Base64.getEncoder().encodeToString(binaryData);
return format("""
{"binaryPayload": "%s"}
""", base64Data);
}
/**
* Decodes state data from JSON string format.
*
* @param binaryPayload the Base64-encoded binary payload
* @param contentType the content type of the stored state
* @return the decoded state data
* @throws IOException if deserialization fails
* @throws ClassNotFoundException if class not found during deserialization
*/
private Map<String, Object> decodeState(byte[] 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()));
}
byte[] bytes = Base64.getDecoder().decode(binaryPayload);
return stateSerializer.dataFromBytes(bytes);
}
private ObjectMapper osonObjectMapper() {
JsonFactory osonFactory = new OsonFactory();
return new ObjectMapper(osonFactory);
}
private void defineCheckpointColumns(PreparedStatement preparedStatement) throws SQLException {
// Defining JSON columns up front avoids an additional Oracle JDBC metadata round trip.
OracleStatement oracleStatement = preparedStatement.unwrap(OracleStatement.class);
oracleStatement.defineColumnType(1, OracleTypes.VARCHAR);View on GitHub (pinned to f82da0b50f)
Solutions
- Clear/recreate the Oracle checkpoint table so old rows written by the previous serializer are removed, then re-run to write checkpoints with the current serializer
- Restore the previous stateSerializer configuration that matches the content type stored in the rows
- Write a migration that deserializes old rows with the old serializer and re-inserts them with the new serializer's content type
- Verify stateSerializer.contentType() is stable and not accidentally varying per instance (e.g. including a random or version value)
Example fix
// before: switched serializer but kept old table new OracleSaver.Builder().dataSource(ds).stateSerializer(new JavaStateSerializer()).build(); // after: align serializer with what stored rows used, or use a fresh table/prefix new OracleSaver.Builder().dataSource(ds).stateSerializer(new JSONStateSerializer()).build(); // matches stored content type
Defensive patterns
Strategy: validation
Validate before calling
// before loading, check stored content type matches the current serializer
String storedType = /* read CONTENT_TYPE column */;
String expectedType = stateSerializer.contentType();
if (!storedType.equals(expectedType)) {
throw new IllegalStateException("Checkpoint table written with serializer type " + storedType
+ " but current serializer produces " + expectedType + "; migrate or clean the table");
} Type guard
boolean isCompatibleSerializer(String storedContentType, StateSerializer<?> serializer) {
return storedContentType != null && storedContentType.equals(serializer.contentType());
} Try / catch
try {
// load checkpoints
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("Content Type used for store state")) {
log.error("Serializer mismatch with stored checkpoints; reconfigure or migrate", e);
// fallback: rebuild state by re-running the workflow
} else { throw e; }
} Prevention
- Pin the state serializer configuration in version-controlled config and keep it stable across releases
- Include the serializer type in deployment/runbook checks when touching checkpoint storage
- Version or segregate checkpoint tables when changing serializers (new table per serializer version)
- Add a startup smoke test that reads one existing checkpoint to detect mismatches early
When it happens
Trigger: Loading a checkpoint whose row was written by a saver configured with a different state serializer (e.g. switching from a JSON serializer to a Java-serialized-object serializer, or a custom serializer whose contentType() changed) and then calling a read path such as list/checkpoint loading that goes through readCheckpoint -> decodeState.
Common situations: Changing the stateSerializer configuration between application versions while reusing an old Oracle checkpoint table; deploying a new release with a different serializer default; multiple application instances sharing one schema but built with different serializer setups.
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 checkpoints
- Unable to load checkpoint
- bytes cannot be empty
- Content Type used for store state '%s' is different from one
- Unable to load checkpoints
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/23d1f752b312371d.
Report an issue: GitHub.