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

	@Override

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Reconfigure the H2Saver to use the same stateSerializer/contentType as when the checkpoints were written
  2. Clear or migrate the old checkpoint data and re-run to regenerate checkpoints
  3. Write a migration that deserializes with the old serializer and re-persists with the new one
  4. 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

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


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