{"record":{"id":"ed4682bee4ce8d6e","repo":"projectdiscovery/nuclei","slug":"circular-include-directive-detected-s","errorCode":null,"errorMessage":"circular include directive detected: %s","messagePattern":"circular include directive detected: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/utils/yaml/preprocess.go","lineNumber":125,"sourceCode":"\n\t\t// pad each line of file content with padBytes\n\t\tincludeFileContent = bytes.ReplaceAll(includeFileContent, []byte(\"\\n\"), padBytes)\n\n\t\t// copy everything up to the directive (including its indentation), then\n\t\t// the expanded content, and resume after the directive.\n\t\tout.Write(data[lastEnd:matchStart])\n\t\tout.Write(includeFileContent)\n\t\tlastEnd = matchEnd\n\t}\n\tout.Write(data[lastEnd:])\n\n\treturn out.Bytes(), nil\n}\n\nfunc readIncludedFile(includeFileName string, includeStack map[string]struct{}, depth int) ([]byte, error) {\n\tincludePath := includePathKey(includeFileName)\n\tif _, ok := includeStack[includePath]; ok {\n\t\treturn nil, fmt.Errorf(\"circular include directive detected: %s\", includeFileName)\n\t}\n\n\tincludeStack[includePath] = struct{}{}\n\tdefer delete(includeStack, includePath)\n\n\tincludeFileContent, err := os.ReadFile(includeFileName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// if it's yaml, tries to preprocess that too recursively\n\tif stringsutil.HasSuffixAny(includeFileName, extensions.YAML) {\n\t\tif depth >= maxIncludeDepth {\n\t\t\treturn nil, fmt.Errorf(\"include directive exceeded maximum include depth of %d\", maxIncludeDepth)\n\t\t}\n\t\tincludeFileContent, err = preProcess(includeFileContent, includeFileName, includeStack, depth+1)\n\t\tif err != nil {\n\t\t\treturn nil, err","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/utils/yaml/preprocess.go#L107-L143","documentation":"The YAML preprocessor detected a circular include chain while expanding include directives. readIncludedFile tracks every file currently on the include stack by absolute path (includePathKey); if the file being included is already on the stack, expansion aborts with this error naming the offending include path.","triggerScenarios":"Template A includes B and B includes A (directly or through a longer chain like A→B→C→A); a file including itself; two files including each other via different relative paths that resolve to the same absolute path.","commonSituations":"Refactoring nuclei template YAML files and accidentally creating mutual references; a shared 'common' file that was later taught to include one of its consumers; symlinks/duplicate paths making a diamond look like a cycle is NOT flagged (only true cycles are), so this almost always means a real loop.","solutions":["Map the include chain: the error names the file that closes the loop; walk includes from the root template to find the back-edge.","Break the cycle by inlining the shared snippet or extracting the mutually-needed part into a third file that both A and B include one-way.","Validate locally with the template-validate target or by loading the template through pkg/utils/yaml Preprocess before shipping.","Never add an include of an ancestor file inside a snippet meant to be embedded."],"exampleFix":"# before\n# a.yaml\ninclude: b.yaml\n# b.yaml\ninclude: a.yaml   # -> circular include directive detected: a.yaml\n\n# after\n# common.yaml holds the shared lines\n# a.yaml\ninclude: common.yaml\n# b.yaml\ninclude: common.yaml","handlingStrategy":"validation","validationCode":"// pre-validate an include tree before preprocessing\nfunc detectCycle(root string, seen map[string]bool) error {\n    abs, _ := filepath.Abs(root)\n    if seen[abs] { return fmt.Errorf(\"circular include at %s\", abs) }\n    seen[abs] = true\n    defer delete(seen, abs)\n    data, err := os.ReadFile(root)\n    if err != nil { return err }\n    for _, m := range includeRe.FindAllStringSubmatch(string(data), -1) {\n        if err := detectCycle(filepath.Join(filepath.Dir(root), m[1]), seen); err != nil { return err }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"content, err := yamlutil.Preprocess(raw, path)\nif err != nil && strings.Contains(err.Error(), \"circular include\") {\n    // template defect: fail validation, never ship/retry\n    return fmt.Errorf(\"bad template %s: %w\", path, err)\n}","preventionTips":["Keep includes one-directional: snippets never include consumers.","Run include-graph validation in template CI.","The detector keys on absolute path — beware copies that hide, not fix, cycles."],"tags":["yaml","include","template","config"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}