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

  1. Materialize once: read the Reader into a String and create the Clob with createClob(String)
  2. Or wrap the source in a mark-capable reader (StringReader, BufferedReader with mark()) before createClob(reader, length)
  3. Consume stream-backed Clobs exactly once; cache the extracted content if you need it again
  4. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/ff85667425ad8ebd. Report an issue: GitHub.