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

  1. Retry the diff/export — mid-walk deletion races are usually transient.
  2. Hold the snapshot exclusively (no concurrent writes/unmounts) while HandleChanges runs.
  3. Check the wrapped errno: fix EACCES with corrected permissions on the upper root path.
  4. 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

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


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/baf7d193791b660b. Report an issue: GitHub.