{"record":{"id":"6c214efa92bb8cb0","repo":"spring-projects/spring-ai","slug":"unsupported-clone-for-messagedigest","errorCode":null,"errorMessage":"Unsupported clone for MessageDigest.","messagePattern":"Unsupported clone for MessageDigest\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-commons/src/main/java/org/springframework/ai/document/id/JdkSha256HexIdGenerator.java","lineNumber":93,"sourceCode":"\t\tAssert.notNull(contents, \"Contents must not be null\");\n\t\ttry (ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {\n\t\t\tObjectOutputStream out = new ObjectOutputStream(byteOut);\n\t\t\tfor (Object content : contents) {\n\t\t\t\tout.writeObject(content);\n\t\t\t}\n\t\t\treturn byteOut.toByteArray();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Failed to serialize\", e);\n\t\t}\n\t}\n\n\tMessageDigest getMessageDigest() {\n\t\ttry {\n\t\t\treturn (MessageDigest) this.messageDigest.clone();\n\t\t}\n\t\tcatch (CloneNotSupportedException e) {\n\t\t\tthrow new RuntimeException(\"Unsupported clone for MessageDigest.\", e);\n\t\t}\n\t}\n\n}\n","sourceCodeStart":75,"sourceCodeEnd":98,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-commons/src/main/java/org/springframework/ai/document/id/JdkSha256HexIdGenerator.java#L75-L98","documentation":"getMessageDigest() clones the generator's prototype MessageDigest so each hash computation gets fresh state; if the digest implementation does not support clone() it throws CloneNotSupportedException, wrapped in RuntimeException('Unsupported clone for MessageDigest.'). Standard SunJCE digests support cloning, so this indicates a custom or provider-specific MessageDigest.","triggerScenarios":"Using a JdkSha256HexIdGenerator constructed with a custom algorithm whose MessageDigest provider implementation does not override/allow clone(), then calling generate()/hashBytes().","commonSituations":"Third-party crypto providers (BouncyCastle in certain modes, hardware/HSM-backed providers) that return non-clonable digest instances.","solutions":["Switch to a JDK-built-in algorithm (SHA-256) via the no-arg constructor","Avoid HSM/provider-backed digests for id generation; use a software provider","Create a new JdkSha256HexIdGenerator per hash instead of reusing the clone path","Catch RuntimeException around generate() and fall back to a fresh instance"],"exampleFix":"// before\nIdGenerator gen = new JdkSha256HexIdGenerator(hsmDigestAlgorithm, UTF_8);\n// after\nIdGenerator gen = new JdkSha256HexIdGenerator(); // SHA-256 from SunJCE, cloneable","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    id = gen.generate(content);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Unsupported clone\")) {\n        id = new JdkSha256HexIdGenerator().generate(content); // software provider fallback\n    } else throw e;\n}","preventionTips":["Use JDK built-in digest algorithms for id generation","Avoid HSM/provider-backed MessageDigest in id generators","Pin a software JCE provider (SunJCE) for hashing paths"],"tags":["hashing","provider","id-generation"],"backgroundTag":"unsupported-operation","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}