spring-projects/spring-ai · error · IllegalArgumentException

Unexpected value type %s!

Error message

Unexpected value type %s!

What it means

The content serializer in MistralAiApi only knows how to serialize content values that are String or List (of ContentChunk). If the message content field holds any other type, IllegalArgumentException('Unexpected value type %s!') is thrown.

Source

Thrown at models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/api/MistralAiApi.java:1540

					jsonGenerator.writeString(text);
				}
				else if (value instanceof List<?> list) {
					jsonGenerator.writeStartArray();

					for (var object : list) {
						if (object instanceof ContentChunk contentChunk) {
							jsonGenerator.writePOJO(contentChunk);
						}
						else {
							throw new IllegalArgumentException(
									"Unexpected value type %s in the list!".formatted(object.getClass()));
						}
					}

					jsonGenerator.writeEndArray();
				}
				else {
					throw new IllegalArgumentException("Unexpected value type %s!".formatted(value.getClass()));
				}
			}

		}

		public static class ContentDeserializer extends ValueDeserializer<Object> {

			@Override
			public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
				var jsonToken = jsonParser.currentToken();

				if (jsonToken == JsonToken.VALUE_STRING) {
					return jsonParser.getValueAsString();
				}

				if (jsonToken == JsonToken.START_ARRAY) {
					List<ContentChunk> contentChunks = new ArrayList<>();

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use String or List<ContentChunk> as the message content type.
  2. Convert unsupported content objects (e.g. byte[] or Media) to String or ContentChunk before building the message.
  3. Locate the code that assigns the offending type via the class name in the error message.

Example fix

// before
new ChatUserMessage(Map.of("key", "value"), null); // unsupported content type
// after
new ChatUserMessage("hello", null); // String content
Defensive patterns

Strategy: type-guard

Validate before calling

Object content = message.getContent();
if (!(content instanceof String) && !(content instanceof List<?>)) {
    throw new IllegalArgumentException("Content must be String or List<ContentChunk>: " + content.getClass());
}

Type guard

boolean isValidContent(Object c) { return c instanceof String || (c instanceof List<?> l && l.stream().allMatch(x -> x instanceof MistralAiApi.ContentChunk)); }

Try / catch

try {
    chatModel.call(prompt);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Unexpected value type")) { /* convert content to String and rebuild prompt */ }
}

Prevention

When it happens

Trigger: Setting a ChatMessage's content to an unsupported object type (e.g. a Map, byte[], or custom class) instead of String or List<ContentChunk> before calling the Mistral API.

Common situations: Custom Prompt converters or memory adapters passing arbitrary content objects; refactoring code that assumed content was always a String.

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 spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/358525e70635976d. Report an issue: GitHub.