glanceapp/glance · error
reading %s: %w
Error message
reading %s: %w
What it means
An I/O failure while reading the main config file (or an included file, since recursiveParseYAMLIncludes is invoked per include) during include resolution. The path is included in the message and the underlying os.ReadFile error is wrapped with %w, exposing the OS-level cause (ENOENT, permission, etc.).
Source
Thrown at internal/glance/config.go:253
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
mainFileContents = configIncludePattern.ReplaceAllFunc(mainFileContents, func(match []byte) []byte {
if includesLastErr != nil {
return nil
}
View on GitHub (pinned to 91324e8de7)
Solutions
- Verify the printed path exists and is spelled correctly (absolute vs relative — relative includes resolve against the including file's directory)
- Check read permission on the file for the glance process user (ls -l, chmod/r chown as needed)
- In containers, confirm the volume actually mounts the file (docker inspect / exec ls)
- If the error names an include target, fix the path in the !include: directive
Example fix
# before !include: ./shared/widgets.yml # file lives in shared/parts/widgets.yml # after !include: ./shared/parts/widgets.yml
Defensive patterns
Strategy: validation
Validate before calling
// Verify readability before loading config
if info, err := os.Stat(cfgPath); err != nil || info.IsDir() {
log.Fatalf("config path unusable: %v", err)
} Try / catch
if err := loadConfig(path); err != nil {
if errors.Is(err, fs.ErrNotExist) {
log.Fatal("config or include file missing: ", err)
}
log.Fatal(err)
} Prevention
- Use absolute config paths in service definitions
- Smoke-test include paths with ls before restart
- Mount config files read-only in containers so paths are stable
When it happens
Trigger: Passing a config path to the reader where the file does not exist, is a directory, or the process lacks read permission; also when an !include: directive points at a missing or unreadable file.
Common situations: Running glance with a wrong -config path; Docker volume mount missing the config file; include paths that were valid on one machine but not another; file permissions after copying configs as a different user.
Related errors
- %s widget: %v
- recursion depth limit of %d reached
- no pages configured
- user has no name
- page %d has no name
AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15).
Data as JSON: /api/errors/c38eec83b4bc2512.
Report an issue: GitHub.