siyuan-note/siyuan · error

stat source [%s] failed: %w

Error message

stat source [%s] failed: %w

What it means

While enumerating legacy iOS workspace entries to move, os.Lstat on a source entry failed with an error other than fs.ErrNotExist. Migration aborts because it cannot determine whether the source exists, avoiding destructive assumptions about user data.

Source

Thrown at kernel/util/working_mobile.go:181

	}

	defaultWorkspaceDir := filepath.Join(workspaceBaseDir, "siyuan")
	destinationEntries, readErr := os.ReadDir(defaultWorkspaceDir)
	if readErr != nil {
		return false, fmt.Errorf("read destination [%s] failed: %w", defaultWorkspaceDir, readErr)
	}
	if 0 < len(destinationEntries) {
		return false, fmt.Errorf("destination [%s] is not empty", defaultWorkspaceDir)
	}

	var moves []workspaceDirMove
	for _, name := range legacyIOSWorkspaceEntries {
		from := filepath.Join(workspaceBaseDir, name)
		if _, statErr := os.Lstat(from); statErr != nil {
			if errors.Is(statErr, fs.ErrNotExist) {
				continue
			}
			return false, fmt.Errorf("stat source [%s] failed: %w", from, statErr)
		}

		to := filepath.Join(defaultWorkspaceDir, name)
		if _, statErr := os.Lstat(to); statErr == nil {
			return false, fmt.Errorf("destination [%s] already exists", to)
		} else if !errors.Is(statErr, fs.ErrNotExist) {
			return false, fmt.Errorf("stat destination [%s] failed: %w", to, statErr)
		}
		moves = append(moves, workspaceDirMove{from: from, to: to})
	}

	var completed []workspaceDirMove
	for _, move := range moves {
		if renameErr := os.Rename(move.from, move.to); renameErr != nil {
			var rollbackErrors []error
			for i := len(completed) - 1; 0 <= i; i-- {
				completedMove := completed[i]
				if rollbackErr := os.Rename(completedMove.to, completedMove.from); rollbackErr != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Read the wrapped %w error to identify the syscall failure (permission vs I/O)
  2. Fix container permissions: reinstall the app or reset its sandbox/documents permissions in system settings
  3. For offloaded iCloud files, download them locally so Lstat/stat can access them
  4. Retry after rebooting the device if it is a transient I/O error
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: probe access to the legacy directory before migrating
if _, err := os.ReadDir(workspaceBaseDir); err != nil {
    return fmt.Errorf("legacy base dir not readable: %w", err)
}

Try / catch

// Go
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && !errors.Is(perr.Err, fs.ErrNotExist) {
        // permission or I/O problem: back off, keep legacy data untouched, surface to user
        return err
    }
}

Prevention

When it happens

Trigger: migrateLegacyIOSWorkspace calls os.Lstat(<workspaceBaseDir>/<name>) and gets a non-ENOENT error (e.g. EACCES, EIO, or a broken symlink edge).

Common situations: Sandbox tightened read permissions on the app container after an OS update; iCloud/offloaded storage making the file inaccessible; filesystem I/O errors on the device.

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 siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/075febb6165065f0. Report an issue: GitHub.