alibaba/spring-ai-alibaba · error · IllegalStateException

Cannot instantiate class {} for @typeHint deserialization

Error message

Cannot instantiate class {} for @typeHint deserialization

What it means

Same mechanism as the @class path: valueFromNode resolves the @typeHint metadata value with Class.forName(typeHint); when that class cannot be loaded, it throws IllegalStateException("Cannot instantiate class <name> for @typeHint deserialization") wrapping the ClassNotFoundException.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/serializer/plain_text/jackson/JacksonDeserializer.java:329

					}
					catch (ClassNotFoundException ex) {
						throw new IllegalStateException(
								"Cannot instantiate class " + className + " for @class deserialization", ex);
					}
				}
				}
				if (typeHint != null) {
					ObjectNode copy = valueNode.deepCopy();
					copy.remove("@typeHint");
					copy.remove(TYPE_PROPERTY);
					copy.remove("@class");
					try {
						Class<?> clazz = Class.forName(typeHint);
						// Use unified deserialization strategy
						yield deserializeWithStrategy(copy, clazz, objectMapper, typeMapper);
					}
					catch (ClassNotFoundException ex) {
						throw new IllegalStateException(
								"Cannot instantiate class " + typeHint + " for @typeHint deserialization", ex);
					}
				}
				Map<String, Object> result = new LinkedHashMap<>();
				var fields = valueNode.fields();
				while (fields.hasNext()) {
					var entry = fields.next();
					String key = entry.getKey();
					if ("@class".equals(key) || "@type".equals(key) || "@typeHint".equals(key)) {
						continue;
					}
					result.put(key, valueFromNode(entry.getValue(), objectMapper, typeMapper));
				}
				yield result;
			}
			case BOOLEAN -> valueNode.asBoolean();
			case NUMBER -> {
				// Preserve original number type logic

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Add the missing class's artifact to the deserializing service's classpath
  2. Keep shared state DTOs in a common module used by both producer and consumer
  3. Align library versions across services so type hints resolve identically
  4. Pre-serialize or migrate old payloads whose @typeHint points to removed classes

Example fix

// before
// pom.xml of consumer lacks the module containing com.acme.TaskResult
Object v = deserializer.valueFromNode(node); // IllegalStateException
// after
<dependency>
  <groupId>com.acme</groupId>
  <artifactId>task-models</artifactId>
  <version>1.2.0</version>
</dependency>
Defensive patterns

Strategy: try-catch

Validate before calling

String hint = valueNode.get("@typeHint").asText();
try {
    Class.forName(hint);
} catch (ClassNotFoundException e) {
    logger.warn("@typeHint {} not on classpath", hint);
}

Type guard

boolean typeHintResolvable(String hint) {
    try { Class.forName(hint); return true; }
    catch (ClassNotFoundException e) { return false; }
}

Try / catch

try {
    return deserializer.valueFromNode(node);
} catch (IllegalStateException e) {
    if (String.valueOf(e.getMessage()).contains("for @typeHint deserialization")) {
        return objectMapper.convertValue(node, Map.class); // degrade gracefully
    }
    throw e;
}

Prevention

When it happens

Trigger: Deserializing JSON where the @typeHint field references a class absent from the runtime classpath — e.g. state serialized with custom typed collections/POJOs then read in an app/module that lacks those classes.

Common situations: Cross-service state exchange where one service has a DTO the other doesn't; version skew between services serializing checkpoints; removed classes after dependency upgrades.

Related errors


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