hibernate/hibernate-orm · error · IllegalArgumentException

Can't render parameter as literal, no literal formatter…

Error message

Can't render parameter as literal, no literal formatter found

What it means

In certain SQL positions Hibernate must render a JDBC parameter inline as a literal (e.g. limit/offset values on dialects that cannot bind parameters there, or inlined static parameters). renderAsLiteral asks the parameter's JdbcMapping for a JdbcLiteralFormatter; if the mapping provides none (common for exotic/basic-java-type mappings), rendering is impossible and this IllegalArgumentException is thrown.

Solutions

  1. Use a standard Java/JDBC type for the parameter (Integer, String, LocalDate...) that has a built-in formatter
  2. For custom types, register a BasicType/JavaType that supplies a JdbcLiteralFormatter
  3. Avoid the construct that forces literal rendering (e.g. use setMaxResults so the dialect takes the native parameter path)
  4. Upgrade Hibernate - formatter coverage for more JDBC types improves by version

Example fix

// before
custom BasicType without literal formatter used where dialect inlines parameters

// after
// register the type with a formatter
BasicType<MyType> t = new MyTypeRenderer(new MyTypeJavaType() {
    @Override public JdbcLiteralFormatter<MyType> getJdbcLiteralFormatter() { return (appender, value, dialect, wrapper) -> appender.appendSql(value.render()); }
});
Defensive patterns

Strategy: fallback

Validate before calling

// Before executing with inlined parameters, confirm the mapping can render literals
JdbcMapping m = (JdbcMapping) param.getExpressionType().getSingleJdbcMapping();
if (m.getJdbcLiteralFormatter() == null) {
    // switch the parameter to a standard type or avoid the literal-rendering construct
}

Try / catch

try {
    query.list();
} catch (IllegalArgumentException e) {
    if ("Can't render parameter as literal, no literal formatter found".equals(e.getMessage())) {
        // rebind the parameter with a standard Java type and retry
    } else throw e;
}

Prevention

When it happens

Trigger: A query whose parameter must be rendered literally (dialect-specific limit emulation, in-clause parameter inlining, or explicit literal rendering mode) where the bound parameter's type has getJdbcLiteralFormatter() == null - e.g. custom BasicType/JavaType without a literal formatter, or certain binary/java-object mappings.

Common situations: Custom UserTypes/BasicTypes that implement the binder but not the literal formatter; pagination on dialects that inline the fetch value; switching a parameter's Java type to one lacking formatter support; Hibernate 6 migration where literal rendering paths expanded.

Related errors


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

Appendix: source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:7656

		if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.NO_UNTYPED ) {
			renderCasted( literal );
		}
		else {
			appendSql( SqlAppender.NULL_KEYWORD );
		}
	}

	protected void renderAsLiteral(JdbcParameter jdbcParameter, Object literalValue) {
		if ( literalValue == null ) {
			renderNull( new QueryLiteral<>( null, (BasicValuedMapping) jdbcParameter.getExpressionType() ) );
		}
		else {
			assert jdbcParameter.getExpressionType().getJdbcTypeCount() == 1;
			final JdbcMapping jdbcMapping = jdbcParameter.getExpressionType().getSingleJdbcMapping();
			//noinspection unchecked
			final JdbcLiteralFormatter<Object> literalFormatter = jdbcMapping.getJdbcLiteralFormatter();
			if ( literalFormatter == null ) {
				throw new IllegalArgumentException( "Can't render parameter as literal, no literal formatter found" );
			}
			else {
				literalFormatter.appendJdbcLiteral( this, literalValue, dialect, getWrapperOptions() );
			}
		}
	}

	@Override
	public void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression) {
		if ( unaryOperationExpression.getOperator() == UnaryArithmeticOperator.UNARY_PLUS ) {
			appendSql( UnaryArithmeticOperator.UNARY_PLUS.getOperatorChar() );
		}
		else {
			appendSql( UnaryArithmeticOperator.UNARY_MINUS.getOperatorChar() );
		}

		unaryOperationExpression.getOperand().accept( this );
	}

View on GitHub (pinned to fad1729dce)