{"record":{"id":"0dfeef6110de6002","repo":"pentaho/pentaho-kettle","slug":"endless-loop-detected-for-substitution-of-variable","errorCode":null,"errorMessage":"Endless loop detected for substitution of variable: ","messagePattern":"Endless loop detected for substitution of variable: ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/util/StringUtil.java","lineNumber":152,"sourceCode":"      if ( j > -1 ) {\n        String varName = rest.substring( i + open.length(), j );\n        Object value = variablesValues.get( varName );\n        if ( value == null ) {\n          value = open + varName + close;\n        } else if ( aString.equals( value ) ) {\n          //Needed to avoid an endless loop if left to continue to next else statement\n          return (String) value;\n        } else {\n          // check for another variable inside this value\n          int another = ( (String) value ).indexOf( open ); // check\n          // here\n          // first for\n          // speed\n          if ( another > -1 ) {\n            // for safety: avoid recursive\n            if ( recursion > 50 ) {\n              // endless loops with stack overflow\n              throw new RuntimeException( \"Endless loop detected for substitution of variable: \" + (String) value );\n            }\n            value = substitute( (String) value, variablesValues, open, close, ++recursion );\n          }\n        }\n        buffer.append( rest.substring( 0, i ) );\n        buffer.append( value );\n        rest = rest.substring( j + close.length() );\n      } else {\n        // no closing tag found; end the search\n        buffer.append( rest );\n        rest = \"\";\n      }\n      // keep searching\n      i = rest.indexOf( open );\n    }\n    buffer.append( rest );\n    return buffer.toString();\n  }","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/util/StringUtil.java#L134-L170","documentation":"StringUtil.substitute (variable substitution engine) throws a RuntimeException when the recursion depth of variable replacement exceeds 50, meaning a variable value references itself directly or through a cycle (e.g. A=${B}, B=${A}). The guard prevents infinite recursion / stack overflow.","triggerScenarios":"A kettle.properties / environment variable whose value contains a reference to itself, or a circular chain of variable references like VAR_A=${VAR_B} and VAR_B=${VAR_A}, expanded via StringUtil.environmentSubstitute / substitute with ${...} (or custom open/close delimiters).","commonSituations":"Setting VAR_HOME=${VAR_HOME}/sub in kettle.properties or a transformation-level parameter referencing itself; copying variables between environments where one was previously resolved and re-registered including its own placeholder.","solutions":["Find the self-referencing or circular variable (the name is appended to the message) and break the cycle.","Rename the intermediate variables so each value references only other, non-circular variables.","Pre-resolve the variable value and store the literal result instead of a placeholder containing itself.","Audit kettle.properties, system properties and transformation/job parameters for repeated variable names."],"exampleFix":"// before\nprops.setProperty(\"JAVA_HOME\", \"${JAVA_HOME}/jdk11\");\n// after\nprops.setProperty(\"JAVA_HOME_PARENT\", \"/opt/java\");\nprops.setProperty(\"JAVA_HOME\", \"${JAVA_HOME_PARENT}/jdk11\");","handlingStrategy":"validation","validationCode":"// detect self-reference before substitution\nString v = variables.getVariable(\"VAR_A\");\nif (v != null && v.contains(\"${VAR_A}\")) throw new KettleException(\"VAR_A references itself\");","typeGuard":null,"tryCatchPattern":"try {\n  result = StringUtil.environmentSubstitute(raw);\n} catch (RuntimeException e) {\n  if (e.getMessage().startsWith(\"Endless loop detected\")) {\n    throw new KettleException(\"Circular variable definition: \" + e.getMessage(), e);\n  }\n  throw e;\n}","preventionTips":["Never define a variable whose value contains its own placeholder.","Keep variable chains short and review kettle.properties after merges.","Unit-test variable substitution graphs for cycles."],"tags":["variables","recursion","infinite-loop","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}