hibernate/hibernate-orm · error · IllegalArgumentException

The SQL function passes an argument at index %s but the frag

Error message

The SQL function passes an argument at index %s but the fragment contains no placeholder for the argument: %s

What it means

HQL's sql("fragment", args...) splices each extra argument into the fragment's next '?' placeholder, scanning left to right. When an argument arrives and no further '?' exists in the remaining fragment, rendering throws IllegalArgumentException naming the argument index and the full fragment.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/function/SqlFunction.java:73

				null
		);
	}

	@Override
	public void render(
			SqlAppender sqlAppender,
			List<? extends SqlAstNode> arguments,
			ReturnableType<?> returnType,
			SqlAstTranslator<?> walker) {
		final QueryLiteral<String> sqlFragmentLiteral = (QueryLiteral<String>) arguments.get( 0 );
		final String sqlFragment = sqlFragmentLiteral.getLiteralValue();
		if ( arguments.size() != 1 ) {
			int index = 0;
			for ( int i = 1; i < arguments.size(); i++ ) {
				final SqlAstNode argument = arguments.get( i );
				final int paramIndex = sqlFragment.indexOf( '?', index );
				if ( paramIndex == -1 ) {
					throw new IllegalArgumentException( "The SQL function passes an argument at index " + i
							+ " but the fragment contains no placeholder for the argument: " + sqlFragment );
				}
				sqlAppender.append( sqlFragment, index, paramIndex );
				argument.accept( walker );
				index = paramIndex + 1;
			}
			sqlAppender.append( sqlFragment, index, sqlFragment.length() );
		}
		else {
			sqlAppender.appendSql( sqlFragment );
		}
	}

	@Override
	public String getArgumentListSignature() {
		return "";
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Count the '?' placeholders in the fragment and the extra arguments — they must match one-to-one in order
  2. Add the missing placeholder or remove the surplus argument
  3. Beware '?' inside string literals of the fragment: they count as placeholders too

Example fix

// before
select sql("coalesce(?, 0)", e.a, e.b) from Entity e

// after
select sql("coalesce(?, ?)", e.a, e.b) from Entity e   -- or sql("coalesce(?, 0)", e.a)
Defensive patterns

Strategy: validation

Validate before calling

// Every extra sql() argument needs exactly one '?' placeholder in the fragment
static void checkSqlFragment(String fragment, int argCount) {
    long placeholders = fragment.chars().filter(c -> c == '?').count();
    if (placeholders != argCount) {
        throw new IllegalArgumentException(
            "sql() fragment has " + placeholders + " placeholder(s) but " + argCount + " argument(s)");
    }
}

Try / catch

try {
    return em.createQuery(hql).getResultList();
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("no placeholder")) {
        throw new QuerySetupException("Mismatch between sql() placeholders and arguments — check fragment template", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: HQL: sql("coalesce(?, 0)", e.a, e.b) — two extra arguments but only one placeholder; also '?' placeholders removed from a fragment while stale arguments remain.

Common situations: Dynamic SQL-fragment builders where fragment and argument list are assembled separately; editing template strings; forgetting that every '?' in the fragment (even inside quoted text) consumes one argument.

Related errors


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