spring-projects/spring-ai · error · RuntimeException
Failed to serialize
Error message
Failed to serialize
What it means
serializeToBytes() serializes id-content objects with Java ObjectOutputStream to hash them; any failure (NotSerializableException, IOException on the internal ByteArrayOutputStream) is wrapped in RuntimeException('Failed to serialize'). Since ByteArrayOutputStream can't actually throw IOException, real failures are almost always non-serializable content passed to generate().
Solutions
- Ensure every object passed to generate() is fully Serializable (including nested values)
- Convert content to a String/byte[] yourself and hash deterministically instead of relying on Java serialization
- Inspect the wrapped cause (NotSerializableException names the offending class)
- Use a different IdGenerator (e.g. RandomIdGenerator) if content isn't serializable
Example fix
// before String id = idGenerator.generate(document.getMetadata()); // metadata holds non-serializable values // after String id = idGenerator.generate(String.valueOf(document.getMetadata())); // plain String, always serializable
Defensive patterns
Strategy: validation
Validate before calling
// ensure content is serializable before hashing
if (!(content instanceof java.io.Serializable)) {
content = String.valueOf(content);
} Type guard
static Object requireSerializable(Object o) {
return o instanceof java.io.Serializable ? o : String.valueOf(o);
} Try / catch
try {
id = gen.generate(content);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Failed to serialize")) {
id = gen.generate(String.valueOf(content));
} else throw e;
} Prevention
- Only pass Serializable content (Strings, numbers, serializable POJOs)
- Avoid lambdas, streams, and framework objects in hashed content
- Check the wrapped NotSerializableException cause to find offending fields
When it happens
Trigger: Calling jdkSha256HexIdGenerator.generate(obj) where obj (or a nested field) does not implement java.io.Serializable, e.g. a Document whose metadata holds a lambda, streams, or third-party non-serializable types.
Common situations: Hashing documents with metadata maps containing ObjectMapper instances, InputStreams, or lambdas captured from method references.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- Unsupported clone for MessageDigest.
- Error serializing documentMap to JSON.
- Failed to convert map to JSON
- Failed to serialize initResponse
- Failed to serialize the Document metadata:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/e7d3a886147aa931.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-commons/src/main/java/org/springframework/ai/document/id/JdkSha256HexIdGenerator.java:84
byte[] hashBytes = getMessageDigest().digest(contentWithMetadata);
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format(this.byteHexFormat, b));
}
return UUID.nameUUIDFromBytes(sb.toString().getBytes(this.charset)).toString();
}
private byte[] serializeToBytes(Object... contents) {
Assert.notNull(contents, "Contents must not be null");
try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {
ObjectOutputStream out = new ObjectOutputStream(byteOut);
for (Object content : contents) {
out.writeObject(content);
}
return byteOut.toByteArray();
}
catch (Exception e) {
throw new RuntimeException("Failed to serialize", e);
}
}
MessageDigest getMessageDigest() {
try {
return (MessageDigest) this.messageDigest.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException("Unsupported clone for MessageDigest.", e);
}
}
}
View on GitHub (pinned to 98a7beda4f)