dagger/dagger · error

unexpected stat type %T

Error message

unexpected stat type %T

What it means

During usage() accounting, the write root is walked and each entry's os.FileInfo is asserted to carry a *syscall.Stat_t via info.Sys(). This holds on real Linux filesystems, but if the FileInfo's underlying system type is anything else the walk aborts with "unexpected stat type %T" (dest_linux.go:490). It is an internal invariant check protecting the inode-deduplication logic (statInode(st)).

Source

Thrown at util/layercopy/dest_linux.go:490

func (d *destination) flush() error {
	return nil
}

func (d *destination) usage() (snapshots.Usage, error) {
	seen := map[inode]struct{}{}
	var usage snapshots.Usage
	err := filepath.WalkDir(d.writeRoot, func(path string, ent fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		info, err := ent.Info()
		if err != nil {
			return err
		}
		st, ok := info.Sys().(*syscall.Stat_t)
		if !ok {
			return fmt.Errorf("unexpected stat type %T", info.Sys())
		}
		ino := statInode(st)
		if _, ok := seen[ino]; ok {
			return nil
		}
		seen[ino] = struct{}{}
		if _, ok := d.crossLinks[ino]; ok {
			return nil
		}
		usage.Inodes++
		usage.Size += st.Blocks * 512
		return nil
	})
	return usage, err
}

func copyMetadata(dstPath, srcPath string, srcInfo os.FileInfo, chown *Ownership, modeOverride *os.FileMode, userxattr bool, xattrErrorHandler XAttrErrorHandler, disableXAttrs bool) error {
	if srcInfo != nil {

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Ensure the write root is on a regular Linux filesystem where Sys() returns *syscall.Stat_t.
  2. In tests, use FileInfo implementations whose Sys() returns a real &syscall.Stat_t{}.
  3. Avoid FileInfo-wrapping middleware between the walk and the copier; pass through the raw os FileInfo.
  4. If a special filesystem must be measured, copy/measure from a bind-mounted real directory instead.

Example fix

// before
type fakeInfo struct{ name string }
func (f fakeInfo) Sys() any { return nil } // breaks Usage()
// after
func (f fakeInfo) Sys() any { return &syscall.Stat_t{Ino: 1, Blocks: 2} }
Defensive patterns

Strategy: validation

Validate before calling

if info, err := filepathWalkProbe(writeRoot); err == nil {
    if _, ok := info.Sys().(*syscall.Stat_t); !ok {
        return fmt.Errorf("write root %s does not provide native stat; Usage unavailable", writeRoot)
    }
}

Type guard

func hasNativeStat(info os.FileInfo) bool {
    _, ok := info.Sys().(*syscall.Stat_t)
    return ok
}

Try / catch

usage, err := cop.Usage()
if err != nil && strings.Contains(err.Error(), "unexpected stat type") {
    // fall back to a plain du-style estimate or skip usage accounting
}

Prevention

When it happens

Trigger: Calling Copier.Usage() when the write root is served by a filesystem/layer whose DirEntry.Info() does not return *syscall.Stat_t — e.g. mock/fake FileInfo in tests, FUSE or network filesystems with wrapped FileInfo implementations, or go1.12+ os.FileInfo wrappers.

Common situations: Unit tests injecting stub FileInfo without Sys() populated; exotic FUSE mounts (s3fs, gocryptfs wrappers) as the upperdir; libraries that wrap os.FileInfo and lose the Sys payload.

Related errors


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