dagger/dagger · error
failed to stat srcPath from differ
Error message
failed to stat srcPath from differ
What it means
After joining the upper root with the change's subPath, the differ calls os.Lstat on the resolved source path; failure here is wrapped with this message. The walk produced a change for a path that no longer exists (or is unstatable) in the upper root, so the diff cannot capture the file's metadata.
Source
Thrown at engine/snapshots/diffapply_linux.go:620
c.srcPath = path
srcfi = stat
break
} else if errors.Is(err, unix.ENOENT) {
continue
} else {
return errors.Wrap(err, "failed to lstat when finding direct path of overlay file")
}
}
default:
srcPath, err := safeJoin(d.upperRoot, subPath)
if err != nil {
return errors.Wrapf(err, "failed to join %s and %s", d.upperRoot, subPath)
}
c.srcPath = srcPath
if fi, err := os.Lstat(c.srcPath); err == nil {
srcfi = fi
} else {
return errors.Wrap(err, "failed to stat srcPath from differ")
}
}
var ok bool
c.srcStat, ok = srcfi.Sys().(*syscall.Stat_t)
if !ok {
return errors.Errorf("unhandled stat type for %+v", srcfi)
}
if !srcfi.IsDir() && c.srcStat.Nlink > 1 {
if linkSubPath, ok := d.inodes[statInode(c.srcStat)]; ok {
c.linkSubPath = linkSubPath
} else {
d.inodes[statInode(c.srcStat)] = c.subPath
}
}
}
View on GitHub (pinned to 82ba2681db)
Solutions
- Retry the diff/export — mid-walk deletion races are usually transient.
- Hold the snapshot exclusively (no concurrent writes/unmounts) while HandleChanges runs.
- Check the wrapped errno: fix EACCES with corrected permissions on the upper root path.
- If reproducible, inspect the specific subPath for leftover whiteouts or broken entries and clean the snapshot.
Defensive patterns
Strategy: retry
Validate before calling
if _, err := os.Lstat(filepath.Join(upperRoot, subPath)); err != nil {
return fmt.Errorf("change target %s missing from upper root: %w", subPath, err)
} Try / catch
err := differ.HandleChanges(ctx, handle)
var pe *os.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, unix.ENOENT) {
time.Sleep(50 * time.Millisecond)
err = differ.HandleChanges(ctx, handle)
}
return err Prevention
- Serialize exports so no concurrent write/delete races the differ
- Keep the container/snapshot alive until HandleChanges returns
- Retry once on ENOENT-class failures
- Check permissions on upperRoot if errno is EACCES rather than ENOENT
When it happens
Trigger: os.Lstat(c.srcPath) fails in the default branch of doubleWalkingChanges: the file was deleted between the lower/upper walk and this stat (race), the upper root changed underneath, or permissions block the stat.
Common situations: Concurrent builds mutating the same snapshot; container teardown racing an image export; running the engine against a snapshot on storage another process is cleaning; permission mismatch after UID change.
Related errors
- failed to stat underlying file from bind mount
- expected exactly one mount, got %d
- failed to lstat when finding direct path of overlay file
- unhandled stat type for %+v
- unexpected type %T
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/baf7d193791b660b.
Report an issue: GitHub.