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

  1. 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
  2. Restore the previous stateSerializer configuration that matches the content type stored in the rows
  3. Write a migration that deserializes old rows with the old serializer and re-inserts them with the new serializer's content type
  4. 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

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


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