glanceapp/glance · error
recursion depth limit of %d reached
Error message
recursion depth limit of %d reached
What it means
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.
Source
Thrown at internal/glance/config.go:248
return strings.TrimSpace(string(fileContents)), false, nil
default:
return "", true, nil
}
}
func formatWidgetInitError(err error, w widget) error {
return fmt.Errorf("%s widget: %v", w.GetType(), err)
}
var configIncludePattern = regexp.MustCompile(`(?m)^([ \t]*)(?:-[ \t]*)?(?:!|\$)include:[ \t]*(.+)$`)
func parseYAMLIncludes(mainFilePath string) ([]byte, map[string]struct{}, error) {
return recursiveParseYAMLIncludes(mainFilePath, nil, 0)
}
func recursiveParseYAMLIncludes(mainFilePath string, includes map[string]struct{}, depth int) ([]byte, map[string]struct{}, error) {
if depth > CONFIG_INCLUDE_RECURSION_DEPTH_LIMIT {
return nil, nil, fmt.Errorf("recursion depth limit of %d reached", CONFIG_INCLUDE_RECURSION_DEPTH_LIMIT)
}
mainFileContents, err := os.ReadFile(mainFilePath)
if err != nil {
return nil, nil, fmt.Errorf("reading %s: %w", mainFilePath, err)
}
mainFileAbsPath, err := filepath.Abs(mainFilePath)
if err != nil {
return nil, nil, fmt.Errorf("getting absolute path of %s: %w", mainFilePath, err)
}
mainFileDir := filepath.Dir(mainFileAbsPath)
if includes == nil {
includes = make(map[string]struct{})
}
var includesLastErr error
View on GitHub (pinned to 91324e8de7)
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
Example fix
# before (cycle: a.yml includes b.yml, b.yml includes a.yml) # a.yml !include: b.yml # b.yml !include: a.yml # after # a.yml !include: widgets.yml # b.yml -> contents inlined into widgets.yml
Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight: detect include cycles/depth before starting
grep -R '!include:' config/ | awk -F'include:' '{print $2}' | sort | uniq -d Try / catch
if _, _, err := parseYAMLIncludes(path); err != nil {
if strings.Contains(err.Error(), "recursion depth limit") {
log.Fatal("include cycle or nesting too deep: ", err)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15).
Data as JSON: /api/errors/9e10968e6954c830.
Report an issue: GitHub.