{"record":{"id":"d5073f30f2626b03","repo":"theonedev/onedev","slug":"circular-template-usages","errorCode":null,"errorMessage":"Circular template usages (","messagePattern":"Circular template usages \\(","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java","lineNumber":498,"sourceCode":"\t\t\t\t\t\t\t\t\t.addBeanNode()\n\t\t\t\t\t\t\t\t\t\t.inIterable().atIndex(importIndex)\n\t\t\t\t\t\t\t\t\t.addConstraintViolation();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisValid = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!isValid)\n\t\t\tcontext.disableDefaultConstraintViolation();\n\t\treturn isValid;\n\t}\n\t\n\tprivate void checkTemplateUsages(UseTemplateStep step, List<String> templateChain) {\n\t\tif(templateChain.contains(step.getTemplateName())) {\n\t\t\ttemplateChain.add(step.getTemplateName());\n\t\t\tthrow new ValidationException(\"Circular template usages (\" + templateChain + \")\");\n\t\t} else {\n\t\t\tStepTemplate template = getStepTemplateMap().get(step.getTemplateName());\n\t\t\tif (template != null) {\n\t\t\t\tif (templateChain.isEmpty()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tParamUtils.validateParamMatrix(template.getParamSpecs(), step.getParamMatrix());\n\t\t\t\t\t\tfor (var paramMap: step.getExcludeParamMaps())\n\t\t\t\t\t\t\tParamUtils.validateParamMap(template.getParamSpecs(), paramMap.getParams());\n\t\t\t\t\t} catch (Exception e) {\n\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()) {","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/buildspec/BuildSpec.java#L480-L516","documentation":"During build spec validation, BuildSpec.checkTemplateUsages walks UseTemplateStep references and maintains a template chain. When a step template indirectly includes itself (directly or through other templates), a ValidationException 'Circular template usages ([...])' is thrown listing the chain. This prevents infinite template expansion at build time.","triggerScenarios":"Defining step template A whose steps include a UseTemplateStep referencing template A, or templates A->B->A; occurs when isValid() is called on a build spec containing such templates.","commonSituations":"Copy-pasting template steps so a template accidentally references itself; renaming templates and leaving a stale self-reference; refactoring common steps into a shared template that both templates include.","solutions":["Open the step templates definition in the build spec and remove the UseTemplateStep that closes the cycle shown in the message's chain","Unfold one level of the cycle by inlining the needed steps instead of reusing the template","Rename templates after refactoring and re-check all UseTemplateStep references","Test template definitions in the build spec editor, which validates templates before save"],"exampleFix":"# before: template 'deploy' includes itself\n- name: deploy\n  steps:\n  - !UseTemplateStep\n    templateName: deploy\n# after\n- name: deploy\n  steps:\n  - !RunCommandStep\n    commands: ./deploy.sh","handlingStrategy":"validation","validationCode":"// ensure no template includes itself, directly or transitively:\n// for each UseTemplateStep in stepTemplates, resolve templateName;\n// fail if a name repeats while following the chain","typeGuard":null,"tryCatchPattern":"try {\n    buildSpec.isValid();\n} catch (ValidationException e) {\n    // e.getMessage() lists the circular chain; remove the offending UseTemplateStep\n}","preventionTips":["Never let a template reference itself, even indirectly","After renaming templates, grep all UseTemplateStep references","Keep shared-step templates acyclic and documented","Validate specs in the editor before committing"],"tags":["buildspec","template","validation","recursion"],"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-14T05:17:10.506Z"}