hibernate/hibernate-orm · error · IllegalStateException

Type for json_table function is already resolved. Mutation i

Error message

Type for json_table function is already resolved. Mutation is not allowed anymore

What it means

SqmJsonTableFunction becomes immutable once its result type is resolved (isTypeResolved() turns true when the AnonymousTupleType for its columns has been computed during query interpretation). checkTypeResolved() guards every structural mutation - adding columns via column()/nested()/ordinalityColumn() - and throws IllegalStateException because the already-computed tuple type would no longer match the definition.

Source

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

		return columns.nested( jsonPath );
	}

	@Override
	public JpaJsonTableFunction ordinalityColumn(String columnName) {
		columns.ordinalityColumn( columnName );
		return this;
	}

	private void addColumn(String columnName) {
		checkTypeResolved();
		if ( !columnNames.add( columnName ) ) {
			throw new IllegalStateException( "Duplicate column: " + columnName );
		}
	}

	private void checkTypeResolved() {
		if ( isTypeResolved() ) {
			throw new IllegalStateException(
					"Type for json_table function is already resolved. Mutation is not allowed anymore" );
		}
	}

	@Override
	public boolean equals(@Nullable Object object) {
		return object instanceof SqmJsonTableFunction<?> that
			&& super.equals( object )
			&& columns.equals( that.columns )
			&& Objects.equals( passingExpressions, that.passingExpressions )
			&& errorBehavior == that.errorBehavior;
	}

	@Override
	public int hashCode() {
		int result = super.hashCode();
		result = 31 * result + columns.hashCode();
		result = 31 * result + Objects.hashCode( passingExpressions );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Finish all columns()/passing()/error-behavior calls before handing the function to createQuery() or executing the query.
  2. Copy the node and mutate the copy - table.copy(SqmCopyContext.simpleContext()) - then add columns to the copy.
  3. Build a fresh json_table function per query instead of reusing an instance.

Example fix

// before - mutate a function already used in a query
t.column( "extra", "$.extra", String.class ); // IllegalStateException: type already resolved

// after - copy first, then mutate the copy
SqmJsonTableFunction<?> t2 = t.copy( SqmCopyContext.simpleContext() );
t2.column( "extra", "$.extra", String.class );
Defensive patterns

Strategy: validation

Validate before calling

private boolean jsonTableUsedInQuery = false;

void afterCreateQuery() {
    jsonTableUsedInQuery = true; // type resolution has happened past this point
}

void mutate(JpaJsonTableFunction<?> t) {
    if ( jsonTableUsedInQuery ) {
        throw new IllegalStateException( "json_table already used - build a new node" );
    }
}

Prevention

When it happens

Trigger: Calling any column-definition method on a json_table node after it was interpreted: reusing one SqmJsonTableFunction across several queries and adding columns for the second one, or mutating a function that a previous createQuery() already processed.

Common situations: Caching SQM fragments or builder objects; shared function instances in request handlers; background mutation of a query tree while another thread renders it; builder-pattern reuse across requests.

Related errors


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