dagger/dagger · warning

readFile %s

Error message

readFile %s

What it means

statFile returns os.ErrNotExist wrapped as "readFile %s" when its walk over the FS completed without producing any entry for the root. This is the not-found signal that readSymlink translates into 'no symlink here' (returns nil targets), so callers usually see the path treated as absent.

Source

Thrown at internal/fsutil/followlinks.go:163

	err := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if p != root {
			return errors.Errorf("expected single entry %q but got %q", root, p)
		}
		out = entry
		if entry.IsDir() {
			return filepath.SkipDir
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	if out == nil {
		return nil, errors.Wrapf(os.ErrNotExist, "readFile %s", root)
	}
	return out, nil
}

func readDir(fs FS, root string) ([]os.DirEntry, error) {
	var out []os.DirEntry

	root = filepath.FromSlash(filepath.Clean(root))
	if root == string(filepath.Separator) || root == "." {
		root = "."
		out = make([]gofs.DirEntry, 0)
	}

	err := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if p == root {

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Confirm each FollowPaths entry exists in the FS at NewFilterFS time; remove stale entries
  2. Avoid following the FS root itself; FollowLinks paths like "/" or "." short-circuit and resolve to nothing meaningful
  3. Handle not-found gracefully in callers — readSymlink already swallows isNotFound, so surface only if it escapes via a different wrap

Example fix

// before
FollowPaths: []string{"bin/dagger"} // missing in snapshot
// after
// ensure bin/dagger exists in the FS before NewFilterFS, or drop it from FollowPaths
Defensive patterns

Strategy: validation

Validate before calling

// Confirm each FollowPaths entry exists before NewFilterFS
for _, p := range opt.FollowPaths {
	if _, err := underlying.Open(filepath.Clean(p)); err != nil {
		return fmt.Errorf("FollowPaths entry missing: %s", p)
	}
}

Try / catch

targets, err := fsutil.FollowLinks(fs, paths)
if err != nil {
	if errors.Is(err, os.ErrNotExist) { return nil, nil } // absent link: no targets
	return err
}

Prevention

When it happens

Trigger: FollowLinks/NewFilterFS resolving a FollowPaths entry (or wildcard match) where statFile's walk finds nothing — the path doesn't exist in the underlying FS snapshot, or the root is "/" or "." which short-circuits to nil entry and then ErrNotExist.

Common situations: FollowPaths listing a symlink that was deleted or never existed in the snapshot, a typo in the followed path, or a wildcard that matched names (via readDir) pointing at paths absent from the FS.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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