theonedev/onedev · error · ValidationException

Circular dependencies (

Error message

Circular dependencies (

What it means

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.

Source

Thrown at server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java:526

						throw new ValidationException(String.format("Error validating step template parameters (%s)", e.getMessage()));
					}
				}
				templateChain.add(step.getTemplateName());
				for (Step templateStep: template.getSteps()) {
					if (templateStep instanceof UseTemplateStep) 
						checkTemplateUsages((UseTemplateStep) templateStep, new ArrayList<>(templateChain));
				}
			} else if (templateChain.isEmpty()) {
				throw new ValidationException("Step template not found (" + step.getTemplateName() + ")");
			}
		}
	}
	
	private void checkDependencies(Job job, List<String> dependencyChain) {
		for (JobDependency dependency: job.getJobDependencies()) {
			if (dependencyChain.contains(dependency.getJobName())) {
				dependencyChain.add(dependency.getJobName());
				throw new ValidationException("Circular dependencies (" + dependencyChain + ")");
			} else {
				Job dependencyJob = getJobMap().get(dependency.getJobName());
				if (dependencyJob != null) {
					if (dependencyChain.isEmpty()) {
						try {
							ParamUtils.validateParamMatrix(dependencyJob.getParamSpecs(), dependency.getParamMatrix());
							for (var paramMap: dependency.getExcludeParamMaps())
								ParamUtils.validateParamMap(dependencyJob.getParamSpecs(), paramMap.getParams());
						} catch (ValidationException e) {
							String message = String.format("Error validating dependency job parameters (dependency job: %s, error message: %s)", 
									dependencyJob.getName(), e.getMessage());
							throw new ValidationException(message);
						}
					}
					List<String> newDependencyChain = new ArrayList<>(dependencyChain);
					newDependencyChain.add(dependency.getJobName());
					checkDependencies(dependencyJob, newDependencyChain);
				} else if (dependencyChain.isEmpty()) {

View on GitHub (pinned to d44925c47c)

Solutions

  1. Break the cycle shown in the message chain by removing one jobDependencies entry
  2. If both jobs genuinely need each other's outputs, merge them into one job or use artifacts/parallel setup instead of dependencies
  3. Review the dependency graph in the build spec editor to visualize cycles before saving

Example fix

# before: cycle a <-> b
- name: a
  jobDependencies: [{jobName: b}]
- name: b
  jobDependencies: [{jobName: a}]
# after
- name: a
  jobDependencies: []
- name: b
  jobDependencies: [{jobName: a}]
Defensive patterns

Strategy: validation

Validate before calling

// topological sort of jobDependencies; if sort fails, a cycle exists

Try / catch

try {
    buildSpec.isValid();
} catch (ValidationException e) {
    // 'Circular dependencies ([a, b, ...])': remove one edge from the listed cycle
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/6e20fb9aed0bc6fb. Report an issue: GitHub.