{"record":{"id":"ff85667425ad8ebd","repo":"hibernate/hibernate-orm","slug":"could-not-reset-reader-ff8566","errorCode":null,"errorMessage":"could not reset reader","messagePattern":"could not reset reader","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/ClobProxy.java","lineNumber":170,"sourceCode":"\t\t\t// total length (this is at odds with the behavior of getSubString(long,int))\n\t\t\tthrow new SQLException( \"Length must be greater than or equal to zero\" );\n\t\t}\n\t\treturn DataHelper.subStream( getCharacterStream(), start-1, (int) length );\n\t}\n\n\t@Override\n\tpublic void free() throws SQLException {\n\t\tcharacterStream.release();\n\t}\n\n\tprotected void resetIfNeeded() {\n\t\ttry {\n\t\t\tif ( needsReset ) {\n\t\t\t\tcharacterStream.asReader().reset();\n\t\t\t}\n\t\t}\n\t\tcatch ( IOException ioe ) {\n\t\t\tthrow new HibernateException( \"could not reset reader\", ioe );\n\t\t}\n\t\tneedsReset = true;\n\t}\n\n\t/**\n\t * Generates a {@link Clob} proxy using the string data.\n\t *\n\t * @param string The data to be wrapped as a {@link Clob}.\n\t *\n\t * @return The generated proxy.\n\t */\n\tpublic static Clob generateProxy(String string) {\n\t\treturn new ClobProxy( string );\n\t}\n\n\t/**\n\t * Generates a {@link Clob} proxy using a character reader of given length.\n\t *","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/ClobProxy.java#L152-L188","documentation":"ClobProxy wraps its character data in a CharacterStream and tracks a needsReset flag so the proxy can be read more than once: before every subsequent read, resetIfNeeded() calls characterStream.asReader().reset(). If the underlying Reader (the one you passed to Hibernate.getLobHelper().createClob(reader, length)) does not support mark/reset, or has been closed via free(), the IOException is wrapped in HibernateException(\"could not reset reader\"). Clobs created from a String use a StringReader and never hit this.","triggerScenarios":"Creating a Clob with createClob(new FileReader(f), len) or any plain Reader, then calling two read operations on it (e.g. getCharacterStream() twice, getSubString() after getAsciiStream(), or two entity flushes reading the value); calling any read after free(); the same stream-backed Clob being consumed by both validation logic and the JDBC binding.","commonSituations":"Stream-backed Clobs read once during validation and again during INSERT; detached entities whose Clob is re-read on merge; wrapping non-buffered readers (Files.newBufferedReader without mark, network readers) that return false from markSupported().","solutions":["Materialize once: read the Reader into a String and create the Clob with createClob(String)","Or wrap the source in a mark-capable reader (StringReader, BufferedReader with mark()) before createClob(reader, length)","Consume stream-backed Clobs exactly once; cache the extracted content if you need it again","Catch HibernateException around repeat reads and rebuild the Clob from a saved copy of the data"],"exampleFix":"// before\nClob clob = session.getLobHelper().createClob(new FileReader(file), file.length());\nclob.getCharacterStream().transferTo(out);   // first read: OK\nclob.getSubString(1, 4);                     // second read: HibernateException: could not reset reader\n\n// after\nString content = Files.readString(file.toPath());\nClob clob = session.getLobHelper().createClob(content); // String-backed: freely re-readable\nclob.getCharacterStream().transferTo(out);\nclob.getSubString(1, 4);","handlingStrategy":"try-catch","validationCode":"// Before creating the Clob, ensure the source reader can be reset\nstatic boolean isRereadable(java.io.Reader reader) {\n    try {\n        reader.mark(1);\n        reader.reset();\n        return true;\n    } catch (IOException e) {\n        return false;\n    }\n}\n// if (!isRereadable(reader)) materialize: createClob(readAll(reader), len)","typeGuard":null,"tryCatchPattern":"try {\n    content = clob.getSubString(1, (int) clob.length());   // second+ read triggers reset\n} catch (org.hibernate.HibernateException e) {\n    if (\"could not reset reader\".equals(e.getMessage())) {\n        // stream-backed clob consumed once: rebuild from a saved copy of the data\n        clob = session.getLobHelper().createClob(savedText);\n        content = clob.getSubString(1, (int) clob.length());\n    } else {\n        throw e;\n    }\n}","preventionTips":["Prefer createClob(String) over createClob(Reader, length) whenever the content fits in memory","If a Reader must be used, wrap it in a mark-capable reader (StringReader/BufferedReader.mark) first","Consume stream-backed Clobs exactly once; keep a materialized copy for any second use","Do not read the Clob after calling free()"],"tags":["hibernate","jdbc","clob","lob","io","stream-reset","reader","hibernate-exception"],"backgroundTag":"stream-reset-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}