{"record":{"id":"6e20fb9aed0bc6fb","repo":"theonedev/onedev","slug":"circular-dependencies","errorCode":null,"errorMessage":"Circular dependencies (","messagePattern":"Circular dependencies \\(","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java","lineNumber":526,"sourceCode":"\t\t\t\t\t\tthrow new ValidationException(String.format(\"Error validating step template parameters (%s)\", e.getMessage()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttemplateChain.add(step.getTemplateName());\n\t\t\t\tfor (Step templateStep: template.getSteps()) {\n\t\t\t\t\tif (templateStep instanceof UseTemplateStep) \n\t\t\t\t\t\tcheckTemplateUsages((UseTemplateStep) templateStep, new ArrayList<>(templateChain));\n\t\t\t\t}\n\t\t\t} else if (templateChain.isEmpty()) {\n\t\t\t\tthrow new ValidationException(\"Step template not found (\" + step.getTemplateName() + \")\");\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void checkDependencies(Job job, List<String> dependencyChain) {\n\t\tfor (JobDependency dependency: job.getJobDependencies()) {\n\t\t\tif (dependencyChain.contains(dependency.getJobName())) {\n\t\t\t\tdependencyChain.add(dependency.getJobName());\n\t\t\t\tthrow new ValidationException(\"Circular dependencies (\" + dependencyChain + \")\");\n\t\t\t} else {\n\t\t\t\tJob dependencyJob = getJobMap().get(dependency.getJobName());\n\t\t\t\tif (dependencyJob != null) {\n\t\t\t\t\tif (dependencyChain.isEmpty()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tParamUtils.validateParamMatrix(dependencyJob.getParamSpecs(), dependency.getParamMatrix());\n\t\t\t\t\t\t\tfor (var paramMap: dependency.getExcludeParamMaps())\n\t\t\t\t\t\t\t\tParamUtils.validateParamMap(dependencyJob.getParamSpecs(), paramMap.getParams());\n\t\t\t\t\t\t} catch (ValidationException e) {\n\t\t\t\t\t\t\tString message = String.format(\"Error validating dependency job parameters (dependency job: %s, error message: %s)\", \n\t\t\t\t\t\t\t\t\tdependencyJob.getName(), e.getMessage());\n\t\t\t\t\t\t\tthrow new ValidationException(message);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tList<String> newDependencyChain = new ArrayList<>(dependencyChain);\n\t\t\t\t\tnewDependencyChain.add(dependency.getJobName());\n\t\t\t\t\tcheckDependencies(dependencyJob, newDependencyChain);\n\t\t\t\t} else if (dependencyChain.isEmpty()) {","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java#L508-L544","documentation":"BuildSpec.checkDependencies validates job dependency graphs and throws ValidationException 'Circular dependencies ([...])' when following JobDependency links returns to a job already in the dependency chain. This prevents unbuildable job graphs where jobs wait on each other forever.","triggerScenarios":"Job A depends on job B and job B (directly or transitively) depends on job A; checked when isValid() validates the spec containing such jobDependencies.","commonSituations":"Adding a dependency to speed up ordering but creating a cycle accidentally; two teams each making their job depend on the other's; copying dependency blocks between jobs without removing the reverse link.","solutions":["Break the cycle shown in the message chain by removing one jobDependencies entry","If both jobs genuinely need each other's outputs, merge them into one job or use artifacts/parallel setup instead of dependencies","Review the dependency graph in the build spec editor to visualize cycles before saving"],"exampleFix":"# before: cycle a <-> b\n- name: a\n  jobDependencies: [{jobName: b}]\n- name: b\n  jobDependencies: [{jobName: a}]\n# after\n- name: a\n  jobDependencies: []\n- name: b\n  jobDependencies: [{jobName: a}]","handlingStrategy":"validation","validationCode":"// topological sort of jobDependencies; if sort fails, a cycle exists","typeGuard":null,"tryCatchPattern":"try {\n    buildSpec.isValid();\n} catch (ValidationException e) {\n    // 'Circular dependencies ([a, b, ...])': remove one edge from the listed cycle\n}","preventionTips":["Keep job dependencies forming a DAG","Sketch the dependency graph before adding cross-job links","Never create mutual dependencies between two jobs","Use the editor's dependency visualization"],"tags":["buildspec","jobs","dependencies","validation","cycle"],"backgroundTag":"schema-validation-failed","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}