hibernate/hibernate-orm · error · HibernateException
could not reset reader
Error message
could not reset reader
What it means
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.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/ClobProxy.java:170
// total length (this is at odds with the behavior of getSubString(long,int))
throw new SQLException( "Length must be greater than or equal to zero" );
}
return DataHelper.subStream( getCharacterStream(), start-1, (int) length );
}
@Override
public void free() throws SQLException {
characterStream.release();
}
protected void resetIfNeeded() {
try {
if ( needsReset ) {
characterStream.asReader().reset();
}
}
catch ( IOException ioe ) {
throw new HibernateException( "could not reset reader", ioe );
}
needsReset = true;
}
/**
* Generates a {@link Clob} proxy using the string data.
*
* @param string The data to be wrapped as a {@link Clob}.
*
* @return The generated proxy.
*/
public static Clob generateProxy(String string) {
return new ClobProxy( string );
}
/**
* Generates a {@link Clob} proxy using a character reader of given length.
*View on GitHub (pinned to fad1729dce)
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
Example fix
// before Clob clob = session.getLobHelper().createClob(new FileReader(file), file.length()); clob.getCharacterStream().transferTo(out); // first read: OK clob.getSubString(1, 4); // second read: HibernateException: could not reset reader // after String content = Files.readString(file.toPath()); Clob clob = session.getLobHelper().createClob(content); // String-backed: freely re-readable clob.getCharacterStream().transferTo(out); clob.getSubString(1, 4);
Defensive patterns
Strategy: try-catch
Validate before calling
// Before creating the Clob, ensure the source reader can be reset
static boolean isRereadable(java.io.Reader reader) {
try {
reader.mark(1);
reader.reset();
return true;
} catch (IOException e) {
return false;
}
}
// if (!isRereadable(reader)) materialize: createClob(readAll(reader), len) Try / catch
try {
content = clob.getSubString(1, (int) clob.length()); // second+ read triggers reset
} catch (org.hibernate.HibernateException e) {
if ("could not reset reader".equals(e.getMessage())) {
// stream-backed clob consumed once: rebuild from a saved copy of the data
clob = session.getLobHelper().createClob(savedText);
content = clob.getSubString(1, (int) clob.length());
} else {
throw e;
}
} Prevention
- 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()
When it happens
Trigger: 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.
Common situations: 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().
Related errors
- Unable to set CLOB string after creation
- Could not create JDBC Clob
- Start position 1-based; must be 1 or more.
- Start position [${start}] cannot exceed overall CLOB length
- Length must be great-than-or-equal to zero.
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/ff85667425ad8ebd.
Report an issue: GitHub.