docker/compose · error
stat %q: %w
Error message
stat %q: %w
What it means
While preparing a tar-based sync, Tar.Sync stats each PathMapping's HostPath to decide whether to copy it or delete its container counterpart. A missing file is expected (treated as a delete), but any other stat failure (permission denied, path too long, I/O error) aborts the whole sync with 'stat %q: %w'. This is a host-filesystem-level error, not a Docker error.
Source
Thrown at internal/sync/tar.go:81
client: client,
}
}
func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) error {
containers, err := t.client.ContainersForService(ctx, t.projectName, service)
if err != nil {
return err
}
var pathsToCopy []PathMapping
var pathsToDelete []string
for _, p := range paths {
if _, err := os.Stat(p.HostPath); err == nil {
pathsToCopy = append(pathsToCopy, *p)
} else if errors.Is(err, fs.ErrNotExist) {
pathsToDelete = append(pathsToDelete, p.ContainerPath)
} else {
return fmt.Errorf("stat %q: %w", p.HostPath, err)
}
}
var deleteCmd []string
if len(pathsToDelete) != 0 {
deleteCmd = append([]string{"rm", "-rf"}, pathsToDelete...)
}
var (
eg errgroup.Group
errMu sync.Mutex
errs = make([]error, 0, len(containers)*2) // max 2 errs per container
)
eg.SetLimit(16) // arbitrary limit, adjust to taste :D
for i := range containers {
containerID := containers[i].ID
tarReader := tarArchive(pathsToCopy)View on GitHub (pinned to ddc4b044b6)
Solutions
- Check permissions on the failing host path (the error's %q names it exactly); chmod/chown the parent directories so the compose process can traverse them
- If the file is intentionally gone, delete the stale entry from your sync path list so it hits the ErrNotExist delete branch cleanly
- Unmount or repair broken network/FUSE filesystems that return EIO on stat
- Re-run compose watch/sync after fixing; if it persists, stat the path manually with the same user to reproduce outside compose
Example fix
# shell: reproduce and fix the exact path named in the error sudo -u $(whoami) stat /path/from/error # -> permission denied sudo chmod o+x /parent/dir # grant traversal # then re-run: docker compose watch
Defensive patterns
Strategy: validation
Validate before calling
// before Tar.Sync: verify every host path is stat-able apart from clean NotExist
for _, p := range paths {
if _, err := os.Stat(p.HostPath); err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("pre-check failed for %s: %w", p.HostPath, err)
}
} Try / catch
// inspect errors from Tar.Sync with errors.Is/As against fs.ErrPermission to distinguish permission vs I/O causes
if err := syncer.Sync(ctx, svc, paths); err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrPermission) { /* fix perms */ }
} Prevention
- Keep sync roots owned by the user running compose
- Avoid syncing into paths containing root-owned artifacts from other containers
- Prefer local filesystems over flaky network mounts for watch/sync sources
When it happens
Trigger: Calling Tar.Sync(ctx, service, paths) where a p.HostPath exists but the process lacks search permission on a parent directory, the path exceeds NAME_MAX, or the underlying filesystem returns an I/O error. Only errors other than fs.ErrNotExist reach this branch.
Common situations: Build contexts or volume-sync roots with restrictive ownership (e.g. files created by a root container then synced by a non-root compose run), paths behind broken FUSE/NFS mounts, or a path that is a dangling symlink whose parent dir is unreadable.
Related errors
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/3e70ac3a75e1d91e.
Report an issue: GitHub.