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

  1. Set every named limit/offset parameter explicitly before execution (query.setParameter("o", off))
  2. Prefer setFirstResult()/setMaxResults() over hand-written offset/fetch parameters in HQL
  3. Clear the query plan cache (hibernate.query.plan_cache_max_size / evict) after dialect or query changes to drop stale plans
  4. 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

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


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