{"record":{"id":"23d1f752b312371d","repo":"alibaba/spring-ai-alibaba","slug":"content-type-used-for-store-state-s-is-differen-23d1f7","errorCode":null,"errorMessage":"Content Type used for store state '%s' is different from one '%s' used for deserialize it","messagePattern":"Content Type used for store state '(.+?)' is different from one '(.+?)' used for deserialize it","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/oracle/OracleSaver.java","lineNumber":337,"sourceCode":"\t\tvar base64Data = Base64.getEncoder().encodeToString(binaryData);\n\t\treturn format(\"\"\"\n\t\t\t\t{\"binaryPayload\": \"%s\"}\n\t\t\t\t\"\"\", base64Data);\n\t}\n\n\t/**\n\t * Decodes state data from JSON string format.\n\t *\n\t * @param binaryPayload the Base64-encoded binary payload\n\t * @param contentType   the content type of the stored state\n\t * @return the decoded state data\n\t * @throws IOException            if deserialization fails\n\t * @throws ClassNotFoundException if class not found during deserialization\n\t */\n\tprivate Map<String, Object> decodeState(byte[] binaryPayload, String contentType)\n\t\t\tthrows IOException, ClassNotFoundException {\n\t\tif (!Objects.equals(contentType, stateSerializer.contentType())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\tformat(\"Content Type used for store state '%s' is different from one '%s' used for deserialize it\",\n\t\t\t\t\t\t\tcontentType,\n\t\t\t\t\t\t\tstateSerializer.contentType()));\n\t\t}\n\n\t\tbyte[] bytes = Base64.getDecoder().decode(binaryPayload);\n\t\treturn stateSerializer.dataFromBytes(bytes);\n\t}\n\n\tprivate ObjectMapper osonObjectMapper() {\n\t\tJsonFactory osonFactory = new OsonFactory();\n\t\treturn new ObjectMapper(osonFactory);\n\t}\n\n\tprivate void defineCheckpointColumns(PreparedStatement preparedStatement) throws SQLException {\n\t\t// Defining JSON columns up front avoids an additional Oracle JDBC metadata round trip.\n\t\tOracleStatement oracleStatement = preparedStatement.unwrap(OracleStatement.class);\n\t\toracleStatement.defineColumnType(1, OracleTypes.VARCHAR);","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/oracle/OracleSaver.java#L319-L355","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before: switched serializer but kept old table\nnew OracleSaver.Builder().dataSource(ds).stateSerializer(new JavaStateSerializer()).build();\n// after: align serializer with what stored rows used, or use a fresh table/prefix\nnew OracleSaver.Builder().dataSource(ds).stateSerializer(new JSONStateSerializer()).build(); // matches stored content type","handlingStrategy":"validation","validationCode":"// before loading, check stored content type matches the current serializer\nString storedType = /* read CONTENT_TYPE column */;\nString expectedType = stateSerializer.contentType();\nif (!storedType.equals(expectedType)) {\n    throw new IllegalStateException(\"Checkpoint table written with serializer type \" + storedType\n        + \" but current serializer produces \" + expectedType + \"; migrate or clean the table\");\n}","typeGuard":"boolean isCompatibleSerializer(String storedContentType, StateSerializer<?> serializer) {\n    return storedContentType != null && storedContentType.equals(serializer.contentType());\n}","tryCatchPattern":"try {\n    // load checkpoints\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Content Type used for store state\")) {\n        log.error(\"Serializer mismatch with stored checkpoints; reconfigure or migrate\", e);\n        // fallback: rebuild state by re-running the workflow\n    } else { throw e; }\n}","preventionTips":["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"],"tags":["checkpoint","deserialization","oracle","state-serializer","content-type"],"backgroundTag":"incompatible-source-type","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}