{"record":{"id":"9e10968e6954c830","repo":"glanceapp/glance","slug":"recursion-depth-limit-of-d-reached","errorCode":null,"errorMessage":"recursion depth limit of %d reached","messagePattern":"recursion depth limit of (.+?) reached","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/glance/config.go","lineNumber":248,"sourceCode":"\t\treturn strings.TrimSpace(string(fileContents)), false, nil\n\tdefault:\n\t\treturn \"\", true, nil\n\t}\n}\n\nfunc formatWidgetInitError(err error, w widget) error {\n\treturn fmt.Errorf(\"%s widget: %v\", w.GetType(), err)\n}\n\nvar configIncludePattern = regexp.MustCompile(`(?m)^([ \\t]*)(?:-[ \\t]*)?(?:!|\\$)include:[ \\t]*(.+)$`)\n\nfunc parseYAMLIncludes(mainFilePath string) ([]byte, map[string]struct{}, error) {\n\treturn recursiveParseYAMLIncludes(mainFilePath, nil, 0)\n}\n\nfunc recursiveParseYAMLIncludes(mainFilePath string, includes map[string]struct{}, depth int) ([]byte, map[string]struct{}, error) {\n\tif depth > CONFIG_INCLUDE_RECURSION_DEPTH_LIMIT {\n\t\treturn nil, nil, fmt.Errorf(\"recursion depth limit of %d reached\", CONFIG_INCLUDE_RECURSION_DEPTH_LIMIT)\n\t}\n\n\tmainFileContents, err := os.ReadFile(mainFilePath)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"reading %s: %w\", mainFilePath, err)\n\t}\n\n\tmainFileAbsPath, err := filepath.Abs(mainFilePath)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"getting absolute path of %s: %w\", mainFilePath, err)\n\t}\n\tmainFileDir := filepath.Dir(mainFileAbsPath)\n\n\tif includes == nil {\n\t\tincludes = make(map[string]struct{})\n\t}\n\tvar includesLastErr error\n","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/config.go#L230-L266","documentation":"Raised when recursive YAML include resolution (!include: / $include: directives) exceeds CONFIG_INCLUDE_RECURSION_DEPTH_LIMIT. Each nested include file is itself scanned for includes; depth counts nesting levels. This is a guard against include cycles (a.yml includes b.yml which includes a.yml) and runaway chains.","triggerScenarios":"Calling parseYAMLIncludes (or reading config from a file) where the include chain nests deeper than the limit: file A includes B, B includes C, and so on past the depth limit, or two files include each other in a cycle.","commonSituations":"An accidental circular include after refactoring config into shared snippets; intentionally deep include hierarchies for shared widget lists; includes generated by tooling that keeps nesting.","solutions":["Check whether two included files include each other (cycle) and break the cycle by inlining one of them","Flatten the hierarchy: have the top-level file include the leaves directly instead of through intermediaries","If the depth is legitimate, reduce nesting by merging small include files into their parent"],"exampleFix":"# before (cycle: a.yml includes b.yml, b.yml includes a.yml)\n# a.yml\n!include: b.yml\n# b.yml\n!include: a.yml\n# after\n# a.yml\n!include: widgets.yml\n# b.yml -> contents inlined into widgets.yml","handlingStrategy":"validation","validationCode":"# Pre-flight: detect include cycles/depth before starting\ngrep -R '!include:' config/ | awk -F'include:' '{print $2}' | sort | uniq -d","typeGuard":null,"tryCatchPattern":"if _, _, err := parseYAMLIncludes(path); err != nil {\n    if strings.Contains(err.Error(), \"recursion depth limit\") {\n        log.Fatal(\"include cycle or nesting too deep: \", err)\n    }\n}","preventionTips":["Keep include hierarchies shallow (2 levels)","Never have two files include each other","Document the include graph in a comment at the top of the main config"],"tags":["glance","yaml","include","recursion","configuration"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}