{"record":{"id":"661208ef329812b5","repo":"pentaho/pentaho-kettle","slug":"basestep-safemode-exception-varyingsize","errorCode":"BaseStep.SafeMode.Exception.VaryingSize","errorMessage":"BaseStep.SafeMode.Exception.VaryingSize","messagePattern":"BaseStep\\.SafeMode\\.Exception\\.VaryingSize","errorType":"validation","errorClass":"KettleRowException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java","lineNumber":2171,"sourceCode":"        outputRowSetsLock.writeLock().unlock();\n      }\n    }\n  }\n\n  /**\n   * Safe mode checking.\n   *\n   * @param referenceRowMeta the reference row meta\n   * @param rowMeta          the row meta\n   * @throws KettleRowException the kettle row exception\n   */\n  public static void safeModeChecking( RowMetaInterface referenceRowMeta, RowMetaInterface rowMeta )\n    throws KettleRowException {\n    // See if the row we got has the same layout as the reference row.\n    // First check the number of fields\n    //\n    if ( referenceRowMeta.size() != rowMeta.size() ) {\n      throw new KettleRowException( BaseMessages.getString( PKG, \"BaseStep.SafeMode.Exception.VaryingSize\", \"\"\n        + referenceRowMeta.size(), \"\" + rowMeta.size(), rowMeta.toString() ) );\n    } else {\n      // Check field by field for the position of the names...\n      for ( int i = 0; i < referenceRowMeta.size(); i++ ) {\n        ValueMetaInterface referenceValue = referenceRowMeta.getValueMeta( i );\n        ValueMetaInterface compareValue = rowMeta.getValueMeta( i );\n\n        if ( !referenceValue.getName().equalsIgnoreCase( compareValue.getName() ) ) {\n          throw new KettleRowException( BaseMessages.getString(\n            PKG, \"BaseStep.SafeMode.Exception.MixingLayout\", \"\" + ( i + 1 ), referenceValue.getName()\n              + \" \" + referenceValue.toStringMeta(), compareValue.getName()\n              + \" \" + compareValue.toStringMeta() ) );\n        }\n\n        if ( referenceValue.getType() != compareValue.getType() ) {\n          throw new KettleRowException( BaseMessages.getString( PKG, \"BaseStep.SafeMode.Exception.MixingTypes\", \"\"\n            + ( i + 1 ), referenceValue.getName() + \" \" + referenceValue.toStringMeta(), compareValue.getName()\n            + \" \" + compareValue.toStringMeta() ) );","sourceCodeStart":2153,"sourceCodeEnd":2189,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java#L2153-L2189","documentation":"safeModeChecking compares each incoming row's RowMetaInterface against the reference row (typically the first row seen). If the field count differs, KettleRowException is thrown. This catches steps that emit rows with inconsistent arity, which would otherwise corrupt row sets.","triggerScenarios":"Safe mode enabled; a step emits a first row with N fields and a later row with a different number of fields (e.g. conditional putRow calls, variable-length JSON/CSV parsing).","commonSituations":"A JavaScript/UDJE step that adds fields only under some conditions; a streaming source whose schema changed mid-run; a splitter step that sometimes emits fewer columns for malformed input lines.","solutions":["Fix the producing step so every row has the same number of fields (always add all fields, using null for missing values).","Log the row layout at the failing step boundary (e.g. with a 'Stream Lookup' or copy of getFields) to find which path emits the wrong arity.","For parsing steps, configure explicit column definitions so output arity is fixed regardless of input content.","If a legitimately variable schema is required, disable safe mode and handle rows defensively downstream."],"exampleFix":"// before\nif (value != null) { outputRow = RowDataUtil.addRowData(inputRow, 1, new Object[]{value}); }\nelse { outputRow = inputRow; } // wrong arity\n// after\noutputRow = RowDataUtil.addRowData(inputRow, 1, new Object[]{value}); // null kept, arity constant","handlingStrategy":"validation","validationCode":"if (rowMeta.size() != expectedSize) throw new IllegalStateException(\"Row has \" + rowMeta.size() + \" fields, expected \" + expectedSize);","typeGuard":null,"tryCatchPattern":"try { putRow(outputRowMeta, row); } catch (KettleRowException e) { if (e.getMessage().contains(\"VaryingSize\")) { logError(\"Inconsistent row arity; fix producing step\"); } throw e; }","preventionTips":["Always allocate all output fields per row, using null placeholders","Use RowDataUtil.addRowData with a fixed-size reservation","Never conditionally skip field creation in processRow","Test custom steps with both empty and populated branches"],"tags":["kettle","pentaho","safe-mode","row-metadata","arity-mismatch"],"backgroundTag":"schema-validation-failed","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"}