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

  1. Check whether two included files include each other (cycle) and break the cycle by inlining one of them
  2. Flatten the hierarchy: have the top-level file include the leaves directly instead of through intermediaries
  3. 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

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.