ipfs/kubo · error

reading stash directory: %w

Error message

reading stash directory: %w

What it means

`findLatestStash` wraps any error from `listStashes(dir)` — which does `os.ReadDir` on `<IPFS_PATH>/stash/` — with this message while trying to locate the most recent backup for revert/rollback. It means the stash directory itself could not be read, not that it is empty (that produces a separate error).

Source

Thrown at core/commands/update.go:694

	slices.SortFunc(stashes, func(a, b stashEntry) int {
		// Sort newest first: if a > b return -1.
		if a.parsed.GreaterThan(b.parsed) {
			return -1
		}
		if b.parsed.GreaterThan(a.parsed) {
			return 1
		}
		return 0
	})

	return stashes, nil
}

// findLatestStash finds the most recently versioned stash file.
func findLatestStash(dir string) (path, ver string, err error) {
	stashes, err := listStashes(dir)
	if err != nil {
		return "", "", fmt.Errorf("reading stash directory: %w", err)
	}
	if len(stashes) == 0 {
		return "", "", fmt.Errorf("no stashed binaries found in %s", dir)
	}
	return stashes[0].path, stashes[0].ver, nil
}

// replaceBinary atomically replaces the binary at targetPath with data.
func replaceBinary(targetPath string, data []byte) error {
	af, err := atomicfile.New(targetPath, 0o755)
	if err != nil {
		return err
	}

	if _, err := af.Write(data); err != nil {
		_ = af.Abort()
		return err
	}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Check the stash dir exists and is readable: `ls -la "$IPFS_PATH/stash"` (default `~/.ipfs/stash`).
  2. Run revert as the user who owns IPFS_PATH (never sudo), or fix ownership: `chown -R $USER "$IPFS_PATH/stash"`.
  3. Verify IPFS_PATH/IPFS_HOME point at the same repo used for the original update.
  4. If no update was ever performed, revert is not possible — reinstall the desired kubo version directly instead.

Example fix

// before
sudo ipfs update revert   # stash dir owned by root: reading stash directory: permission denied

// after
sudo chown -R $USER: "$IPFS_PATH/stash"
ipfs update revert
Defensive patterns

Strategy: validation

Validate before calling

stashDir := filepath.Join(repoPath, "stash")
if _, err := os.ReadDir(stashDir); err != nil {
	// no stash directory: revert is impossible; skip or inform the user
}
if fi, err := os.Stat(stashDir); err == nil && fi.Mode()&0o400 == 0 {
	// unreadable: fix ownership/permissions before invoking revert
}

Type guard

func stashReadable(repoPath string) bool {
	d := filepath.Join(repoPath, "stash")
	fi, err := os.Stat(d)
	return err == nil && fi.IsDir() && fi.Mode().Perm()&0o400 != 0
}

Try / catch

path, ver, err := findLatestStash(dir)
if err != nil {
	if strings.Contains(err.Error(), "reading stash directory") {
		// distinguish missing/unreadable stash from empty stash
		if _, statErr := os.Stat(dir); os.IsNotExist(statErr) {
			return errors.New("never updated; nothing to revert to")
		}
	}
	return fmt.Errorf("cannot revert: %w", err)
}

Prevention

When it happens

Trigger: `ipfs update revert`/`rollback` when the stash directory does not exist (never updated before), IPFS_PATH points at a wrong/nonexistent path, or the directory has permissions denying read access to the current user.

Common situations: Running revert without ever running an update; IPFS_HOME/IPFS_PATH env var pointing elsewhere than where the update ran; stash directory owned by root after a sudo-run update; directory deleted by cleanup tooling.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/bed72def474d46a8. Report an issue: GitHub.