{"record":{"id":"015b456cc5f2e056","repo":"docker/compose","slug":"cannot-watch-root-directory","errorCode":null,"errorMessage":"cannot watch root directory","messagePattern":"cannot watch root directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/watch/paths.go","lineNumber":28,"sourceCode":"   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\n\npackage watch\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc greatestExistingAncestor(path string) (string, error) {\n\tif path == string(filepath.Separator) ||\n\t\tpath == fmt.Sprintf(\"%s%s\", filepath.VolumeName(path), string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"cannot watch root directory\")\n\t}\n\n\t_, err := os.Stat(path)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn \"\", fmt.Errorf(\"os.Stat(%q): %w\", path, err)\n\t}\n\n\tif os.IsNotExist(err) {\n\t\treturn greatestExistingAncestor(filepath.Dir(path))\n\t}\n\n\treturn path, nil\n}\n","sourceCodeStart":10,"sourceCodeEnd":42,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/watch/paths.go#L10-L42","documentation":"greatestExistingAncestor walks a path upward until it finds a directory that exists, so the watcher has something concrete to attach to. It refuses to go all the way to the top: if the path is exactly the filesystem root ('/' on unix, or 'C:\\' style volume roots on Windows), watching it is meaningless and dangerous, so it returns this sentinel error instead.","triggerScenarios":"Passing a watch path whose every ancestor is missing until only the root remains — e.g. '/nonexistent/deeply/nested/path' on unix where even '/' is the only existing ancestor, or a bare volume root like 'D:\\' on Windows. Called from greatestExistingAncestors when building the watch list for `docker compose watch`/develop.","commonSituations":"Typo in an absolute bind/watch path so nothing under it exists; a bind mount source that is created only later by another step; developing on Windows with a path whose drive letter has no such directory; containerized setups where the host path does not exist inside the mount namespace.","solutions":["Fix the path: verify with `ls`/`Test-Path` that the directory you intend to watch actually exists.","If the directory is created at runtime, create it before starting `docker compose watch` (mkdir -p in your start script).","For compose files, correct the develop.watch.source path so it points at a real directory in the project."],"exampleFix":"# before (compose.yaml)\ndevelop:\n  watch:\n    - path: /srv/ap/src   # does not exist\n      action: sync\n# after\ndevelop:\n  watch:\n    - path: ./src\n      action: sync","handlingStrategy":"validation","validationCode":"// guard watch paths before building watchers\np = filepath.Clean(p)\nif p == string(filepath.Separator) || p == filepath.VolumeName(p)+string(filepath.Separator) {\n    return fmt.Errorf(\"refusing to watch filesystem root\")\n}\nif _, err := os.Stat(p); errors.Is(err, fs.ErrNotExist) && filepath.Dir(p) == p {\n    return fmt.Errorf(\"path %q has no existing ancestor\", p)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use project-relative paths for develop.watch sources instead of absolute paths.","Create watched directories in setup scripts before starting compose watch.","Never pass filesystem roots or bare drive letters as watch paths."],"tags":["go","docker-compose","watch","filesystem","path-validation"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}