wagoodman/dive · error

unable to open file %q: %s

Error message

unable to open file %q: %s

What it means

Raised as a panic in NewFileInfoFromPath (dive/filetree/file_info.go:72). For every non-directory entry the function opens the file with os.Open(realPath) to hash its contents; if the open fails, the library panics with 'unable to open file %q: %s'. Hashing is mandatory for regular files, so any open failure aborts the process.

Source

Thrown at dive/filetree/file_info.go:72

		fileType = tar.TypeSymlink

		linkName, err = os.Readlink(realPath)
		if err != nil {
			panic(fmt.Errorf("unable to read symlink %q: %s", realPath, err))
		}
	} else if info.IsDir() {
		fileType = tar.TypeDir
	} else {
		fileType = tar.TypeReg

		size = info.Size()
	}

	var hash uint64
	if fileType != tar.TypeDir {
		file, err := os.Open(realPath)
		if err != nil {
			panic(fmt.Errorf("unable to open file %q: %s", realPath, err))
		}
		defer file.Close()
		hash = getHashFromReader(file)
	}

	return FileInfo{
		Path:     path,
		TypeFlag: fileType,
		Linkname: linkName,
		hash:     hash,
		Size:     size,
		Mode:     info.Mode(),
		// todo: support UID/GID
		Uid:   -1,
		Gid:   -1,
		IsDir: info.IsDir(),
	}
}

View on GitHub (pinned to d6c691947f)

Solutions

  1. Make the tree static before analysis: stop the writing process, or copy/snapshot the directory first (cp -a / rsync) and scan the copy
  2. Check permissions on the failing path: ls -l <path>; fix ownership or run dive as a user that can read it
  3. Prune volatile/unreadable subtrees from the walk
  4. As a library consumer, pre-check readability with os.Open before calling NewFileInfoFromPath and skip unreadable entries

Example fix

// before: assume every regular file is openable
fi := filetree.NewFileInfoFromPath(p, real, info) // panics if open fails

// after: gate on an explicit readability probe
if info.Mode().IsRegular() {
    f, err := os.Open(real)
    if err != nil {
        log.Printf("skipping unreadable file %s: %v", real, err)
        return nil
    }
    f.Close()
}
fi := filetree.NewFileInfoFromPath(p, real, info)
Defensive patterns

Strategy: validation

Validate before calling

// probe readability before hashing path
func openable(realPath string, info os.FileInfo) bool {
    if !info.Mode().IsRegular() {
        return true // dirs are never opened
    }
    f, err := os.Open(realPath)
    if err != nil {
        return false
    }
    f.Close()
    return true
}

if !openable(realPath, info) {
    return nil // skip unreadable file instead of panicking
}

Try / catch

// recover-based guard for library consumers driving their own walk:
defer func() {
    if r := recover(); r != nil {
        log.Printf("skipping %s: %v", realPath, r)
    }
}()
fi := filetree.NewFileInfoFromPath(path, realPath, info)

Prevention

When it happens

Trigger: File deleted, renamed, or chmod'd between the walk's lstat and dive's os.Open (race); file with mode 000 or otherwise unreadable by the effective user; a FIFO/socket/device that opens but behaves oddly; path longer than PATH_MAX on some filesystems.

Common situations: Scanning a live container overlay or build output directory that changes mid-scan, scanning files owned by another user without root, and scanning directories containing sockets/FIFOs created by running daemons.

Related errors


AI-assisted analysis of wagoodman/dive@d6c691947f (2026-08-15). Data as JSON: /api/errors/2d6bd0c1b22d3026. Report an issue: GitHub.