gastownhall/beads · error

failed to inspect %s: %w

Error message

failed to inspect %s: %w

What it means

fixBeadsDirPermissions starts with os.Lstat on the beads directory; this error wraps any Lstat failure other than not-exist (which is treated as 'nothing to fix'). It means the path could not be inspected — typically a permission problem on a parent directory, a broken link situation Lstat itself reports, or an I/O error. The underlying error is preserved with %w.

Source

Thrown at internal/config/permissions.go:54

// FixBeadsDirPermissions sets the .beads directory to BeadsDirPerm when it
// has group or world-accessible bits. Returns true if permissions changed.
func FixBeadsDirPermissions(path string) (bool, error) {
	return fixBeadsDirPermissions(path, openBeadsDirHandle)
}

type beadsDirHandle interface {
	Stat() (os.FileInfo, error)
	Chmod(os.FileMode) error
	Close() error
}

func fixBeadsDirPermissions(path string, openDir func(string) (beadsDirHandle, error)) (bool, error) {
	info, err := os.Lstat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil // directory doesn't exist yet
		}
		return false, fmt.Errorf("failed to inspect %s: %w", path, err)
	}
	if info.Mode()&os.ModeSymlink != 0 {
		return false, fmt.Errorf("refusing to chmod %s: path is a symbolic link", path)
	}
	if !info.IsDir() {
		return false, fmt.Errorf("refusing to chmod %s: path is not a directory", path)
	}
	perm := info.Mode().Perm()
	if perm&0077 == 0 {
		return false, nil // no group or world-accessible bits
	}

	dir, err := openDir(path)
	if err != nil {
		return false, fmt.Errorf("failed to open %s securely: %w", path, err)
	}
	defer func() { _ = dir.Close() }()

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check permissions on each path component leading to the beads directory (need execute/search on parents).
  2. Resolve any symlink loops: `namei -l <path>` or inspect with ls -l; remove the cycle.
  3. Check whether the volume holding the path is mounted and healthy (dmesg / mount output).
  4. If running in a container or as another user, run with sufficient privileges or fix ownership of the parent directories.

Example fix

// before
fixBeadsDirPermissions("/home/otheruser/.beads", ...) // parent not searchable
// after
chmod o+x /home/otheruser   # restore search permission on parent
# then retry FixBeadsDirPermissions
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check traversability before the fix call:
for d := filepath.Dir(path); ; d = filepath.Dir(d) {
    if _, err := os.Stat(d); err != nil {
        return fmt.Errorf("path component %s unreachable: %w", d, err)
    }
    if d == filepath.Dir(d) { break }
}

Try / catch

fixed, err := config.FixBeadsDirPermissions(beadsDir)
if err != nil {
    if strings.Contains(err.Error(), "failed to inspect") {
        return fmt.Errorf("cannot access %s: %w — check parent dir permissions and symlinks", beadsDir, err)
    }
    return err
}

Prevention

When it happens

Trigger: os.Lstat on the beads dir path fails with an error that is not os.IsNotExist — e.g. EACCES on a parent directory, ELOOP from a symlink cycle, or EIO on the filesystem.

Common situations: A parent directory was chmod'd to remove search permission; a symlink loop in the path; a network/external volume went offline; running as a user without traverse rights (common in containers or multi-user machines).

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/6bfb25fc1f21d51a. Report an issue: GitHub.