{"record":{"id":"e669610e94a5ca36","repo":"apache/hadoop","slug":"job-control-has-circular-dependency","errorCode":null,"errorMessage":"job control has circular dependency","messagePattern":"job control has circular dependency","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/jobcontrol/JobControl.java","lineNumber":215,"sourceCode":"    if (this.runnerState == ThreadState.SUSPENDED) {\n      this.runnerState = ThreadState.RUNNING;\n    }\n  }\n\t\n  synchronized public boolean allFinished() {\n    return jobsInProgress.isEmpty();\n  }\n\t\n  /**\n   *  The main loop for the thread.\n   *  The loop does the following:\n   *  \tCheck the states of the running jobs\n   *  \tUpdate the states of waiting jobs\n   *  \tSubmit the jobs in ready state\n   */\n  public void run() {\n    if (isCircular(jobsInProgress)) {\n      throw new IllegalArgumentException(\"job control has circular dependency\");\n    }\n    try {\n      this.runnerState = ThreadState.RUNNING;\n      while (true) {\n        while (this.runnerState == ThreadState.SUSPENDED) {\n          try {\n            Thread.sleep(5000);\n          }\n          catch (Exception e) {\n            //TODO the thread was interrupted, do something!!!\n          }\n        }\n        \n        synchronized(this) {\n          Iterator<ControlledJob> it = jobsInProgress.iterator();\n          while(it.hasNext()) {\n            ControlledJob j = it.next();\n            LOG.debug(\"Checking state of job \"+j);","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/jobcontrol/JobControl.java#L197-L233","documentation":"Error \"job control has circular dependency\" thrown in apache/hadoop.","triggerScenarios":"Thrown by JobControl.addJobs() when the dependency graph built from ControlledJob dependencies contains a cycle, so no topological ordering exists and the jobs can never all become ready. Fix by removing the circular ControlledJob.addDependingJob() chain.","commonSituations":"A job is accidentally added as its own dependency; two jobs each list the other in addDependingJob(); programmatically generated job graphs that create a cycle.","solutions":["Remove the circular dependency between the jobs: inspect the Job objects added to JobControl and break the cycle so every job's dependency graph is a DAG.","Re-check calls to ControlledJob.addDependingJob() and ensure a job never (directly or transitively) depends on itself."],"exampleFix":"JobControl control = new JobControl(\"group\");\nControlledJob a = new ControlledJob(conf);\nControlledJob b = new ControlledJob(conf);\nb.addDependingJob(a); // correct: a runs before b; do NOT also call a.addDependingJob(b)\ncontrol.addJob(a);\ncontrol.addJob(b);","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}