{"record":{"id":"4bc183f743e0b9de","repo":"hibernate/hibernate-orm","slug":"null-is-an-illegal-value-for-cycle-mark-values","errorCode":null,"errorMessage":"Null is an illegal value for cycle mark values!","messagePattern":"Null is an illegal value for cycle mark values!","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/cte/SqmCteStatement.java","lineNumber":312,"sourceCode":"\t}\n\n\t@Override\n\tpublic <X> void cycleUsing(\n\t\t\tString cycleMarkAttributeName,\n\t\t\tString cyclePathAttributeName,\n\t\t\tX cycleValue,\n\t\t\tX noCycleValue,\n\t\t\tList<JpaCteCriteriaAttribute> cycleAttributes) {\n\t\tif ( cycleMarkAttributeName == null || cycleAttributes == null || cycleAttributes.isEmpty() ) {\n\t\t\tthis.cycleMarkAttributeName = null;\n\t\t\tthis.cyclePathAttributeName = null;\n\t\t\tthis.cycleValue = null;\n\t\t\tthis.noCycleValue = null;\n\t\t\tthis.cycleAttributes = Collections.emptyList();\n\t\t}\n\t\telse {\n\t\t\tif ( cycleValue == null || noCycleValue == null ) {\n\t\t\t\tthrow new IllegalArgumentException( \"Null is an illegal value for cycle mark values!\" );\n\t\t\t}\n\t\t\tfinal SqmExpression<X> cycleValueLiteral = nodeBuilder().literal( cycleValue );\n\t\t\tfinal SqmExpression<X> noCycleValueLiteral = nodeBuilder().literal( noCycleValue );\n\t\t\tif ( cycleValueLiteral.getNodeType() != noCycleValueLiteral.getNodeType() ) {\n\t\t\t\tthrow new IllegalArgumentException( \"Inconsistent types for cycle mark values: [\" + cycleValueLiteral.getNodeType() + \", \" + noCycleValueLiteral.getNodeType() + \"]\" );\n\t\t\t}\n\t\t\tfinal List<SqmCteTableColumn> attributes = new ArrayList<>( cycleAttributes.size() );\n\t\t\tfor ( JpaCteCriteriaAttribute cycleAttribute : cycleAttributes ) {\n\t\t\t\tif ( !cteTable.getAttributes().contains( cycleAttribute ) ) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Illegal cycle attribute '\" +\n\t\t\t\t\t\t\t\t\t( cycleAttribute == null ? \"null\" : cycleAttribute.getName() ) +\n\t\t\t\t\t\t\t\t\t\"' passed, which is not part of the JpaCteCriteria!\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tattributes.add( (SqmCteTableColumn) cycleAttribute );\n\t\t\t}\n\t\t\tthis.cycleMarkAttributeName = cycleMarkAttributeName;","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/cte/SqmCteStatement.java#L294-L330","documentation":"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.","triggerScenarios":"cte.cycle(\"isCycle\", \"path\", null, null, columns), or cte.cycle(\"isCycle\", \"path\", true, null, columns) on a recursive CTE created with withRecursiveUnionAll/Distinct.","commonSituations":"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.","solutions":["Pass explicit non-null mark values of one type, e.g. true/false, 1/0 or \"T\"/\"F\".","If defaults are fine, use the shorter overloads cycle(cycleMarkAttributeName, cycleColumns) / cycleUsing(...) which do not take mark values."],"exampleFix":"// before\ncte.cycle( \"isCycle\", \"path\", null, null, columns ); // null mark values\n// after\ncte.cycle( \"isCycle\", \"path\", true, false, columns );","handlingStrategy":"validation","validationCode":"Objects.requireNonNull( cycleValue, \"cycleValue\" );\nObjects.requireNonNull( noCycleValue, \"noCycleValue\" );\ncte.cycle( \"isCycle\", \"path\", cycleValue, noCycleValue, columns );","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to a canonical pair such as true/false instead of nulls.","Use the short cycle(cycleMarkAttributeName, cycleColumns)/cycleUsing overloads when default marks suffice.","Treat null as 'clause not wanted' only by skipping the cycle() call entirely."],"tags":["hibernate","criteria-api","cte","cycle-clause","recursive-query","null-check"],"backgroundTag":"null-argument-validation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}