{"record":{"id":"09103cf874d8a252","repo":"googleapis/mcp-toolbox","slug":"error-reading-config-folder-w","errorCode":null,"errorMessage":"error reading config folder %w","messagePattern":"error reading config folder %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/root.go","lineNumber":199,"sourceCode":"}\n\n// Helper to check if a file has a newer ModTime than stored in the map\nfunc checkModTime(path string, mTime time.Time, lastSeen map[string]time.Time) bool {\n\tif mTime.After(lastSeen[path]) {\n\t\tlastSeen[path] = mTime\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Helper to scan watched files and check their modification times in polling system\nfunc scanWatchedFiles(watchingFolder bool, folderToWatch string, watchedFiles map[string]bool, lastSeen map[string]time.Time) (map[string]bool, bool, error) {\n\tchanged := false\n\tcurrentDiskFiles := make(map[string]bool)\n\tif watchingFolder {\n\t\tfiles, err := os.ReadDir(folderToWatch)\n\t\tif err != nil {\n\t\t\treturn nil, changed, fmt.Errorf(\"error reading config folder %w\", err)\n\t\t}\n\t\tfor _, f := range files {\n\t\t\tif !f.IsDir() && (strings.HasSuffix(f.Name(), \".yaml\") || strings.HasSuffix(f.Name(), \".yml\")) {\n\t\t\t\tfullPath := filepath.Join(folderToWatch, f.Name())\n\t\t\t\tcurrentDiskFiles[fullPath] = true\n\t\t\t\tif info, err := f.Info(); err == nil {\n\t\t\t\t\tif checkModTime(fullPath, info.ModTime(), lastSeen) {\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor f := range watchedFiles {\n\t\t\tif info, err := os.Stat(f); err == nil {\n\t\t\t\tcurrentDiskFiles[f] = true\n\t\t\t\tif checkModTime(f, info.ModTime(), lastSeen) {\n\t\t\t\t\tchanged = true","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/cmd/root.go#L181-L217","documentation":"`scanWatchedFiles` throws \"error reading config folder %w\" when `os.ReadDir(folderToWatch)` fails while scanning the watched config directory for changed .yaml/.yml files during dynamic reload. This is a filesystem-level failure — the folder could not be listed at all — and wraps the underlying os error (which names the reason: not found, permission denied, etc.). The caller `watchChanges` receives the error and can no longer detect config changes.","triggerScenarios":"`toolbox serve` is run with folder watching enabled (watching a directory rather than a single file) and the directory was deleted, renamed, is on an unmounted volume, or the process lacks read permission on it; `scanWatchedFiles` is invoked on each watch tick.","commonSituations":"Config directory moved/deleted while the server runs; running in a container where the config dir mount disappeared; permission changes (chmod/chown) after startup; pointing `--watch-folder` (or equivalent) at a path that doesn't exist.","solutions":["Read the wrapped OS error — it states whether the path is missing, permission-denied, or not a directory","Verify `folderToWatch` exists and is a directory: `ls -la <folder>`","Restore read permission on the folder for the toolbox process user (`chmod/chown`)","If the folder was deleted/moved, recreate it or restart the server pointing at the correct path","For containers, ensure the config volume mount is present and not read-removed at runtime"],"exampleFix":"// before: server started watching a folder that was later renamed\ntoolbox serve --config-folder ./configs   // ./configs deleted at runtime\n// after: recreate the folder or restart against the right path\nmkdir -p ./configs && toolbox serve --config-folder ./configs","handlingStrategy":"validation","validationCode":"// Check the watch folder exists and is readable before starting the server\nfunc watchable(dir string) error {\n\tinfo, err := os.Stat(dir)\n\tif err != nil { return err }\n\tif !info.IsDir() { return fmt.Errorf(\"%s is not a directory\", dir) }\n\tf, err := os.Open(dir)\n\tif err != nil { return err }\n\treturn f.Close()\n}","typeGuard":null,"tryCatchPattern":"files, err := os.ReadDir(folderToWatch)\nif err != nil {\n\treturn nil, changed, fmt.Errorf(\"error reading config folder %w\", err)\n}","preventionTips":["Confirm the config folder exists, is a directory, and is readable before enabling watch mode","Don't delete/rename the watched folder at runtime; recreate it if needed and re-save a file to re-trigger scanning","In containers, verify the config volume stays mounted for the server's lifetime","Keep folder permissions stable (avoid chmod/chown changes while watching)"],"tags":["go","filesystem","watch","hot-reload","os"],"backgroundTag":"config-folder-unreadable","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}