docker/compose · error

cannot watch root directory

Error message

cannot watch root directory

What it means

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.

Source

Thrown at pkg/watch/paths.go:28

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package watch

import (
	"fmt"
	"os"
	"path/filepath"
)

func greatestExistingAncestor(path string) (string, error) {
	if path == string(filepath.Separator) ||
		path == fmt.Sprintf("%s%s", filepath.VolumeName(path), string(filepath.Separator)) {
		return "", fmt.Errorf("cannot watch root directory")
	}

	_, err := os.Stat(path)
	if err != nil && !os.IsNotExist(err) {
		return "", fmt.Errorf("os.Stat(%q): %w", path, err)
	}

	if os.IsNotExist(err) {
		return greatestExistingAncestor(filepath.Dir(path))
	}

	return path, nil
}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Fix the path: verify with `ls`/`Test-Path` that the directory you intend to watch actually exists.
  2. If the directory is created at runtime, create it before starting `docker compose watch` (mkdir -p in your start script).
  3. For compose files, correct the develop.watch.source path so it points at a real directory in the project.

Example fix

# before (compose.yaml)
develop:
  watch:
    - path: /srv/ap/src   # does not exist
      action: sync
# after
develop:
  watch:
    - path: ./src
      action: sync
Defensive patterns

Strategy: validation

Validate before calling

// guard watch paths before building watchers
p = filepath.Clean(p)
if p == string(filepath.Separator) || p == filepath.VolumeName(p)+string(filepath.Separator) {
    return fmt.Errorf("refusing to watch filesystem root")
}
if _, err := os.Stat(p); errors.Is(err, fs.ErrNotExist) && filepath.Dir(p) == p {
    return fmt.Errorf("path %q has no existing ancestor", p)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/015b456cc5f2e056. Report an issue: GitHub.