{"record":{"id":"a9546df7ef14de80","repo":"pentaho/pentaho-kettle","slug":"a-deadlock-was-detected-between-steps-0-and-1-the-steps-are","errorCode":null,"errorMessage":"A deadlock was detected between steps '{0}' and '{1}'.  The steps are both waiting for each other because a series of row set buffers filled up.","messagePattern":"A deadlock was detected between steps '(.+?)' and '(.+?)'\\.  The steps are both waiting for each other because a series of row set buffers filled up\\.","errorType":"exception","errorClass":"KettleStepException","httpStatus":null,"severity":"critical","filePath":"engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java","lineNumber":2374,"sourceCode":"          inputSize += rowSet.size();\n        }\n        // All full probably means a stalled step.\n        List<RowSet> combiOutputRowSets = combi.step.getOutputRowSets();\n        if ( inputSize > 0 && inputSize == totalSize && combiOutputRowSets.size() > 1 ) {\n          RowSet outputFull = null;\n          RowSet outputEmpty = null;\n          for ( RowSet rowSet : combiOutputRowSets ) {\n            if ( rowSet.size() == transMeta.getSizeRowset() ) {\n              outputFull = rowSet;\n            } else if ( rowSet.size() == 0 ) {\n              outputEmpty = rowSet;\n            }\n          }\n          if ( outputFull != null && outputEmpty != null ) {\n            // Verify that this step is lated before the current one\n            //\n            if ( transMeta.findPrevious( stepMeta, combi.stepMeta ) ) {\n              throw new KettleStepException( \"A deadlock was detected between steps '\"\n                + combi.stepname + \"' and '\" + stepname\n                + \"'.  The steps are both waiting for each other because a series of row set buffers filled up.\" );\n            }\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Find input row set.\n   *\n   * @param sourceStep the source step\n   * @return the row set\n   * @throws KettleStepException the kettle step exception\n   */\n  public RowSet findInputRowSet( String sourceStep ) throws KettleStepException {\n    // Check to see that \"sourceStep\" only runs in a single copy","sourceCodeStart":2356,"sourceCodeEnd":2392,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java#L2356-L2392","documentation":"During transformation startup, BaseStep detects that two steps that depend on each other have both filled their row set buffers and are waiting for each other — a deadlock in the step graph. KettleStepException is thrown when transMeta.findPrevious confirms the peer step is actually an upstream dependency of the current step.","triggerScenarios":"A transformation where step A reads from step B's rowset while B (directly or via a cycle) reads from A, and both row sets are full; detected in the row set fullness audit while steps run single-threaded waiting on getRow/putRow.","commonSituations":"Accidental circular hop (A->B->A) drawn in Spoon; a feedback loop without a 'Stream Lookup'-style buffering design; 'Block until steps finish' combined with cross-reading between mutually dependent sub-transformations.","solutions":["Remove the cyclic hop in the transformation canvas so the step graph is a DAG.","If a feedback loop is intended, break the cycle with a blocking/cacheing step (e.g. Stream Lookup with a cache, or write to a file/DB and read back in a separate transformation).","Split the transformation into two transformations executed in sequence (a Job with two Transformation entries).","Review 'copies' and hop directions in the failing pair reported in the message and confirm which row set filled first."],"exampleFix":"// before (step graph)\nA -> B -> A  (cycle)\n// after\nA -> B ; B -> file ; job step: transformation2 reads file","handlingStrategy":"try-catch","validationCode":"// Before running: detect cycles in the step graph\nList<StepMeta> hops = transMeta.getSteps();\njava.util.Set<StepMeta> visited = new java.util.HashSet<>();\nfor (StepMeta s : hops) { if (hasCycle(transMeta, s, visited)) throw new IllegalStateException(\"Cyclic hop detected involving \" + s.getName()); }","typeGuard":null,"tryCatchPattern":"try { trans.prepareExecution(new Object[0]); trans.startThreads(); trans.waitUntilFinished(); } catch (KettleException e) { if (e.getMessage() != null && e.getMessage().contains(\"deadlock was detected\")) { logError(\"Cycle between steps; refactor to DAG\"); } throw e; }","preventionTips":["Never draw hops that close a loop between two steps","Use a Job with sequential transformations for feedback patterns","Review hop arrows in Spoon after copy-pasting step groups","Keep 'Block until steps finish' usage minimal and acyclic"],"tags":["kettle","pentaho","deadlock","rowset","transformation"],"backgroundTag":"invalid-state-transition","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"}