{"record":{"id":"37f8f55770452f60","repo":"docker/compose","slug":"notify-add-q-w","errorCode":null,"errorMessage":"notify.Add(%q): %w","messagePattern":"notify\\.Add\\(%q\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/watch/watcher_naive.go","lineNumber":82,"sourceCode":"\t}\n\n\tpathsToWatch := []string{}\n\tfor path := range d.notifyList {\n\t\tpathsToWatch = append(pathsToWatch, path)\n\t}\n\n\tpathsToWatch, err := greatestExistingAncestors(pathsToWatch)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif d.isWatcherRecursive {\n\t\tpathsToWatch = pathutil.EncompassingPaths(pathsToWatch)\n\t}\n\n\tfor _, name := range pathsToWatch {\n\t\tfi, err := os.Stat(name)\n\t\tif err != nil && !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"notify.Add(%q): %w\", name, err)\n\t\t}\n\n\t\t// if it's a file that doesn't exist,\n\t\t// we should have caught that above, let's just skip it.\n\t\tif os.IsNotExist(err) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif fi.IsDir() {\n\t\t\terr = d.watchRecursively(name)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"notify.Add(%q): %w\", name, err)\n\t\t\t}\n\t\t} else {\n\t\t\terr = d.addWatch(filepath.Dir(name))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"notify.Add(%q): %w\", filepath.Dir(name), err)\n\t\t\t}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/watch/watcher_naive.go#L64-L100","documentation":"In naiveNotify.Add (the non-recursive fallback watcher used when fsnotify lacks recursive support), each candidate path is stat'ed before a watch is added. A stat error that is neither nil nor IsNotExist — permission denial or I/O error — is wrapped with the path name. This mirrors greatestExistingAncestor's logic but happens at registration time.","triggerScenarios":"d.Add(paths) hitting a path whose stat fails with EACCES/EIO while the naive (non-fsevents, non-recursive fsnotify) watcher registers watches. NotNotExist errors are deliberately skipped (missing files were resolved earlier), so only real stat failures land here.","commonSituations":"Watch roots under directories with restrictive permissions; paths on failing disks or stale network mounts; Linux systems without recursive inotify support taking this code path (e.g. older kernels or non-Linux non-macOS platforms).","solutions":["Check the path from the message with ls -ld and grant the current user read/search permission on it.","Remove the inaccessible path from the watch set (fix develop.watch entries) if it is not essential.","Repair the underlying mount/filesystem if stat fails with an I/O error."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify each watch path is stat-able (or confirmed absent) up front\nfor _, p := range paths {\n    if _, err := os.Stat(p); err != nil && !os.IsNotExist(err) {\n        return fmt.Errorf(\"path %q not accessible: %w\", p, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var pathErr *os.PathError\n    if errors.As(err, &pathErr) && os.IsPermission(pathErr) {\n        // report pathErr.Path and advise chmod/chown\n    }\n    return err // non-permission stat failures are environment faults\n}","preventionTips":["Grant the compose process read access to all watch roots and their ancestors.","Keep watch paths off failing or stale network mounts.","Validate watch paths at startup in wrapper scripts."],"tags":["go","docker-compose","watch","filesystem","permissions","inotify"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}