hibernate/hibernate-orm · error · IllegalArgumentException

Couldn't determine types of columns of function 'json_table'

Error message

Couldn't determine types of columns of function 'json_table'

What it means

Hibernate derives json_table's result type as an AnonymousTupleType built from the declared columns (Columns.createTupleType()). If no columns were defined at all (table.columnNames is empty), there is no type to build and it throws IllegalArgumentException('Couldn't determine types of columns of function json_table'). Most dialects require an explicit COLUMNS clause for JSON_TABLE anyway.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/expression/SqmJsonTableFunction.java:1160

			int result = jsonPath.hashCode();
			result = 31 * result + SqmCacheable.cacheHashCode( columnDefinitions );
			return result;
		}
	}

	public static final class Columns extends NestedColumns implements SqmTypedNode<Object> {

		public Columns(SqmJsonTableFunction<?> table) {
			super( "", table );
		}

		private Columns(SqmJsonTableFunction<?> table, ArrayList<ColumnDefinition> columnDefinitions) {
			super( "", table, columnDefinitions );
		}

		public AnonymousTupleType<?> createTupleType() {
			if ( table.columnNames.isEmpty() ) {
				throw new IllegalArgumentException( "Couldn't determine types of columns of function 'json_table'" );
			}
			final SqmBindableType<?>[] componentTypes = new SqmBindableType<?>[table.columnNames.size()];
			final String[] componentNames = new String[table.columnNames.size()];
			int result = populateTupleType( 0, componentNames, componentTypes );

			// Sanity check
			assert result == componentTypes.length;

			return new AnonymousTupleType<>( componentTypes, componentNames );
		}

		@Override
		public Columns copy(SqmCopyContext context) {
			final ArrayList<ColumnDefinition> definitions = new ArrayList<>( columnDefinitions.size() );
			for ( ColumnDefinition columnDefinition : columnDefinitions ) {
				definitions.add( columnDefinition.copy( context ) );
			}
			return new Columns( castNonNull( context.getCopy( table ) ), definitions );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Always declare at least one column (path column or ordinality column) before executing the query.
  2. If you only need to test existence, use json_exists instead of json_table.
  3. Guard dynamic builders: assert that at least one column was added before createQuery().

Example fix

// before
JpaJsonTableFunction<?> t = cb.jsonTable( doc, "$" ); // no columns defined

// after - declare at least one column
cb.jsonTable( doc, "$" ).ordinalityColumn( "n" );   // or a full .columns(...) definition
Defensive patterns

Strategy: validation

Validate before calling

int definedColumns = 0; // increment in every column()/ordinalityColumn() call
if ( definedColumns == 0 ) {
    throw new IllegalStateException( "json_table requires at least one column definition" );
}
return em.createQuery( cq );

Prevention

When it happens

Trigger: Creating a json_table criteria node without any columns clause and letting interpretation proceed: cb.jsonTable(doc, path) with no columns()/ordinalityColumn() call, so the lazily computed tuple type finds zero columns.

Common situations: Builder code that conditionally skips the columns clause; refactors that removed the columns call; assuming a columnless JSON_TABLE behaves like json_exists; adapting SQL examples that omit COLUMNS.

Related errors


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