hibernate/hibernate-orm · error · ExecutionException
JDBC parameter value not bound -
Error message
JDBC parameter value not bound -
What it means
During pagination emulation, some dialects render fetch+offset as one combined JDBC parameter (renderFetchPlusOffsetExpressionAsSingleParameter). A custom binder is registered that resolves the offset parameter's value from JdbcParameterBindings at execution time; if the offset parameter has no binding, this ExecutionException is thrown. It means the SQL contains a limit/offset placeholder whose value was never supplied for this execution.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:4765
protected void renderFetchPlusOffsetExpressionAsSingleParameter(
Expression fetchClauseExpression,
Expression offsetClauseExpression,
int offset) {
if ( fetchClauseExpression instanceof Literal fetchLiteral ) {
final Number fetchCount = (Number) fetchLiteral.getLiteralValue();
if ( offsetClauseExpression instanceof Literal offsetLiteral ) {
final Number offsetCount = (Number) offsetLiteral.getLiteralValue();
appendSql( fetchCount.intValue() + offsetCount.intValue() + offset );
}
else {
final JdbcParameter offsetParameter = (JdbcParameter) offsetClauseExpression;
final int offsetValue = offset + fetchCount.intValue();
final int parameterPosition = addParameterBinder(
offsetParameter,
(statement, startPosition, jdbcParameterBindings, executionContext) -> {
final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( offsetParameter );
if ( binding == null ) {
throw new ExecutionException( "JDBC parameter value not bound - " + offsetParameter );
}
final Number bindValue = (Number) binding.getBindValue();
//noinspection unchecked
offsetParameter.getExpressionType().getSingleJdbcMapping().getJdbcValueBinder().bind(
statement,
bindValue.intValue() + offsetValue,
startPosition,
executionContext.getSession()
);
}
);
renderParameterAsParameter( parameterPosition, offsetParameter );
}
}
else {
final JdbcParameter offsetParameter = (JdbcParameter) offsetClauseExpression;
final JdbcParameter fetchParameter = (JdbcParameter) fetchClauseExpression;
final FetchPlusOffsetParameterBinder fetchBinder = new FetchPlusOffsetParameterBinder(View on GitHub (pinned to fad1729dce)
Solutions
- Set every named limit/offset parameter explicitly before execution (query.setParameter("o", off))
- Prefer setFirstResult()/setMaxResults() over hand-written offset/fetch parameters in HQL
- Clear the query plan cache (hibernate.query.plan_cache_max_size / evict) after dialect or query changes to drop stale plans
- Reproduce with hibernate.query.plan_cache_max_size=0; if it disappears, it is a plan-cache binding issue - upgrade Hibernate
Example fix
// before
Query<Order> q = session.createQuery("from Order o offset :off rows", Order.class);
q.list(); // offset never bound
// after
Query<Order> q = session.createQuery("from Order o", Order.class)
.setFirstResult(off);
q.list(); Defensive patterns
Strategy: validation
Validate before calling
// Before execution, verify every named parameter of a paginated query is bound
for (String np : query.getParameterMetadata().getNamedParameterNames()) {
if (!query.isBound(np)) { // or track bound names yourself
throw new IllegalStateException("Unbound parameter: " + np);
}
} Try / catch
try {
results = query.list();
} catch (org.hibernate.QueryExecutionException | ExecutionException e) {
if (e.getMessage() != null && e.getMessage().startsWith("JDBC parameter value not bound")) {
// log query + pagination settings, ensure setFirstResult/setMaxResults used, then retry once after plan cache evict
} else throw e;
} Prevention
- Always paginate via setFirstResult/setMaxResults instead of HQL offset/fetch parameters
- Set every named parameter before list()/getResultList()
- Evict the query plan cache after dialect config changes
When it happens
Trigger: Executing a paginated query (setFirstResult/setMaxResults or HQL limit/offset with a parameterized offset) on a dialect that merges fetch and offset into a single parameter, where jdbcParameterBindings.getBinding(offsetParameter) returns null - e.g. a named offset parameter that was never set, a stale cached query plan rebound with different parameters, or a code path re-executing a prepared plan without refreshing limit bindings.
Common situations: Using offset/fetch with bind parameters (e.g. hql '... offset :o rows') and forgetting setParameter; query plan cache returning a plan after in-clause parameter expansion changed parameter positions; follow-on locking or scrollable results combined with pagination; upgrading Hibernate where limit emulation switched to the combined-parameter path for the dialect.
Related errors
- Can't render offset and fetch clause for subquery
- Can't render offset and fetch clause for subquery
- Can't emulate fetch clause type:
- Can't emulate offset clause in subquery
- Can't emulate offset clause in subquery
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e39dd4ae80ced6e4.
Report an issue: GitHub.