hibernate/hibernate-orm · error · IllegalArgumentException

Null is an illegal value for cycle mark values!

Error message

Null is an illegal value for cycle mark values!

What it means

The full cycle(...) overload on SqmCteStatement converts cycleValue/noCycleValue into SQM literals via nodeBuilder().literal(...); when a cycle clause is being defined (cycleMarkAttributeName non-null, attributes non-empty) but either mark value is null, it throws IllegalArgumentException. Null cannot be a cycle mark literal, both the cycle and the no-cycle value must be supplied.

Source

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

	}

	@Override
	public <X> void cycleUsing(
			String cycleMarkAttributeName,
			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;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Pass explicit non-null mark values of one type, e.g. true/false, 1/0 or "T"/"F".
  2. If defaults are fine, use the shorter overloads cycle(cycleMarkAttributeName, cycleColumns) / cycleUsing(...) which do not take mark values.

Example fix

// before
cte.cycle( "isCycle", "path", null, null, columns ); // null mark values
// after
cte.cycle( "isCycle", "path", true, false, columns );
Defensive patterns

Strategy: validation

Validate before calling

Objects.requireNonNull( cycleValue, "cycleValue" );
Objects.requireNonNull( noCycleValue, "noCycleValue" );
cte.cycle( "isCycle", "path", cycleValue, noCycleValue, columns );

Prevention

When it happens

Trigger: cte.cycle("isCycle", "path", null, null, columns), or cte.cycle("isCycle", "path", true, null, columns) on a recursive CTE created with withRecursiveUnionAll/Distinct.

Common situations: Trying to let the database use defaults by passing nulls; refactoring from the short cycle(String, JpaCteCriteriaAttribute...) overload to the full one and forgetting to fill in the mark values.

Related errors


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