hibernate/hibernate-orm · error · IllegalArgumentException

Inconsistent types for cycle mark values: [{}, {}]

Error message

Inconsistent types for cycle mark values: [{}, {}]

What it means

cycle(...) wraps cycleValue and noCycleValue in SqmLiteral expressions and compares getNodeType() of the two literals; if the types differ (for example a String mark vs an Integer mark, or boolean vs int) it throws IllegalArgumentException listing both types. The two cycle mark values must map to the same node type so the rendered SQL has consistent column types.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/cte/SqmCteStatement.java:317

			String cyclePathAttributeName,
			X cycleValue,
			X noCycleValue,
			List<JpaCteCriteriaAttribute> cycleAttributes) {
		if ( cycleMarkAttributeName == null || cycleAttributes == null || cycleAttributes.isEmpty() ) {
			this.cycleMarkAttributeName = null;
			this.cyclePathAttributeName = null;
			this.cycleValue = null;
			this.noCycleValue = null;
			this.cycleAttributes = Collections.emptyList();
		}
		else {
			if ( cycleValue == null || noCycleValue == null ) {
				throw new IllegalArgumentException( "Null is an illegal value for cycle mark values!" );
			}
			final SqmExpression<X> cycleValueLiteral = nodeBuilder().literal( cycleValue );
			final SqmExpression<X> noCycleValueLiteral = nodeBuilder().literal( noCycleValue );
			if ( cycleValueLiteral.getNodeType() != noCycleValueLiteral.getNodeType() ) {
				throw new IllegalArgumentException( "Inconsistent types for cycle mark values: [" + cycleValueLiteral.getNodeType() + ", " + noCycleValueLiteral.getNodeType() + "]" );
			}
			final List<SqmCteTableColumn> attributes = new ArrayList<>( cycleAttributes.size() );
			for ( JpaCteCriteriaAttribute cycleAttribute : cycleAttributes ) {
				if ( !cteTable.getAttributes().contains( cycleAttribute ) ) {
					throw new IllegalArgumentException(
							"Illegal cycle attribute '" +
									( cycleAttribute == null ? "null" : cycleAttribute.getName() ) +
									"' passed, which is not part of the JpaCteCriteria!"
					);
				}
				attributes.add( (SqmCteTableColumn) cycleAttribute );
			}
			this.cycleMarkAttributeName = cycleMarkAttributeName;
			this.cyclePathAttributeName = cyclePathAttributeName;
			this.cycleValue = (SqmLiteral<Object>) cycleValueLiteral;
			this.noCycleValue = (SqmLiteral<Object>) noCycleValueLiteral;
			this.cycleAttributes = attributes;
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use a matching pair of the same type: true/false, 1/0 or "Y"/"N".
  2. Type the helper method as <X> and take both values as X so the compiler enforces identical types.

Example fix

// before
cte.cycle( "isCycle", "path", true, 1, columns ); // boolean vs int
// after
cte.cycle( "isCycle", "path", true, false, columns );
Defensive patterns

Strategy: validation

Validate before calling

static <X> void assertSameType(X cycleValue, X noCycleValue) {
    if ( !cycleValue.getClass().equals( noCycleValue.getClass() ) ) {
        throw new IllegalArgumentException( "Cycle mark values must share one type" );
    }
}
// then: cte.cycle( "m", "p", v1, v2, cols );

Prevention

When it happens

Trigger: cte.cycle("isCycle", "path", true, 1, columns), or ("isCycle", "path", "T", 0, columns): any pair where the values have different runtime types producing different literal node types.

Common situations: Loosely-typed helper code where the two values come from different sources (config vs constant); changing one literal ('Y' vs 1) during refactoring; boxed types that differ between the two parameters.

Related errors


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