docker/compose · error
finding ancestor of %s: %w
Error message
finding ancestor of %s: %w
What it means
This error is thrown by Docker Compose's file watcher (docker compose watch / up --watch) when it cannot resolve a watch path to an existing directory. Before subscribing to filesystem events, the naive (fsnotify-based) watcher walks each configured path upward through its ancestors to find the closest one that exists, so it can watch that ancestor and detect when the missing path is later created. If that walk fails for any path, the wrapped cause is reported with this message. The two underlying causes produced by greatestExistingAncestor (pkg/watch/paths.go:25) are 'cannot watch root directory' — the walk reached / or a Windows volume root without finding an existing ancestor — and an os.Stat failure other than not-exist (e.g. permission denied, I/O error) on the path or one of its parents.
Source
Thrown at pkg/watch/watcher_naive.go:348
watcher: fsw,
events: fsw.Events,
wrappedEvents: wrappedEvents,
errors: fsw.Errors,
isWatcherRecursive: isWatcherRecursive,
}
wmw.addWatch = wmw.add
return wmw, nil
}
var _ Notify = &naiveNotify{}
func greatestExistingAncestors(paths []string) ([]string, error) {
result := []string{}
for _, p := range paths {
newP, err := greatestExistingAncestor(p)
if err != nil {
return nil, fmt.Errorf("finding ancestor of %s: %w", p, err)
}
result = append(result, newP)
}
return result, nil
}
View on GitHub (pinned to ddc4b044b6)
Solutions
- Check the failing path (named in the error message after 'ancestor of') against your compose file's develop section and bind-mount sources; fix typos or create the missing directory with mkdir -p.
- Ensure watch paths are host-side absolute or correctly relative paths, not container-internal paths, and that '~' is expanded to $HOME before being handed to compose.
- Never configure / (or a bare Windows drive like C:\) as a watch path — the watcher explicitly refuses to watch the root directory; narrow it to a project subdirectory.
- If the cause is a stat error such as permission denied, fix filesystem permissions on the path and its parents (or remount the network volume) and confirm with 'stat <path>' as the same user that runs compose.
- Re-run docker compose watch and confirm the error is gone; use docker compose config to render the resolved develop section and verify the paths.
Example fix
// docker-compose.yml — before
develop:
watch:
- action: sync
path: ./src
target: /app/src
// error: finding ancestor of /proj/src: cannot watch root directory
// (./src does not exist on host, so ancestor walk reached /)
// after — create the watched host directory, or point at one that exists
mkdir -p ./src
docker compose watch Defensive patterns
Strategy: validation
Validate before calling
// Before starting docker compose watch, verify each watch path
// has at least one existing ancestor and is not a filesystem root.
func validateWatchPath(path string) error {
abs, err := filepath.Abs(path)
if err != nil {
return fmt.Errorf("resolving %s: %w", path, err)
}
if abs == string(filepath.Separator) || abs == filepath.VolumeName(abs)+string(filepath.Separator) {
return fmt.Errorf("%s is a filesystem root; cannot be watched", abs)
}
for p := abs; ; p = filepath.Dir(p) {
if _, err := os.Stat(p); err == nil {
return nil // found an existing ancestor
} else if !os.IsNotExist(err) {
return fmt.Errorf("stat %q: %w", p, err) // permission/IO problem
}
if p == filepath.Dir(p) {
return fmt.Errorf("no existing ancestor of %s", abs)
}
}
} Type guard
// Go callers of docker/compose/v5/pkg/watch directly:
type rootWatchError struct{ path string }
func (e rootWatchError) Error() string { return "cannot watch root directory" }
// errors.As / errors.Is cannot match the anonymous fmt.Errorf in
// greatestExistingAncestor, so match on the wrapped text instead:
func isWatchAncestorError(err error) bool {
return err != nil && strings.Contains(err.Error(), "finding ancestor of ")
} Try / catch
// In Go, treat watcher startup as fatal but report the offending path:
if err := watcher.Start(); err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) && os.IsPermission(pathErr.Err) {
log.Fatalf("watch: fix permissions on %s: %v", pathErr.Path, err)
}
if strings.Contains(err.Error(), "cannot watch root directory") {
log.Fatalf("watch: refusing to watch filesystem root; narrow the develop.watch path")
}
log.Fatalf("watch: %v", err)
} Prevention
- Keep every develop.watch path inside the project directory and commit (or mkdir at setup) the watched directories so a fresh clone always has them.
- Run 'docker compose config' to render the resolved develop section and confirm each path is a real host path before invoking watch.
- Expand ~ and environment variables in watch paths yourself; compose does not create missing host directories for you.
- Never use / or a bare drive letter as a watch root; choose the deepest directory that actually exists.
- On network/permission-sensitive mounts, verify 'stat <path>' succeeds as the compose user before starting watch.
When it happens
Trigger: Running 'docker compose watch' or 'docker compose up --watch' where a develop.watch path (regular expression or explicit path in the compose file's develop section, or a bind-mount source without a corresponding host directory) resolves to / , a Windows drive root like C:\, or a path whose entire ancestor chain does not exist (e.g. /nonexistent/deeply/nested/dir on a fresh checkout). Also triggered when os.Stat on the path or an ancestor fails with EACCES/EPERM (unreadable parent directory) or an I/O error, for example when the project lives on a network mount, a FUSE volume, or a directory with restrictive permissions.
Common situations: A develop.watch path pointing to a directory that was never created or is gitignored and missing after clone; a path typo so no ancestor matches anything real; a path anchored at the filesystem root; running the watcher against a container path instead of the host path; home-directory expansion (~) not expanded so the literal '~' directory does not exist; permission-restricted environments (root-squashed NFS, SELinux denials) where stat on a parent fails; CI environments where expected volumes are not mounted.
Related errors
- OCI remote resource is disabled by %q
- initializing remote resource cache: %w
- error reading .dockerignore: %w
- cannot watch root directory
- os.Stat(%q): %w
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/0387a9c631ae63f7.
Report an issue: GitHub.