siyuan-note/siyuan · error
stat destination [%s] failed: %w
Error message
stat destination [%s] failed: %w
What it means
While pre-checking moves, os.Lstat on a destination path inside <base>/siyuan returned an error other than fs.ErrNotExist (i.e. not a clean 'does not exist' answer). Migration aborts because the existence check itself failed, so it cannot guarantee a safe rename.
Source
Thrown at kernel/util/working_mobile.go:188
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 {
rollbackErrors = append(rollbackErrors, fmt.Errorf("rollback [%s] to [%s] failed: %w",
completedMove.to, completedMove.from, rollbackErr))
}
}
return false, errors.Join(append([]error{fmt.Errorf("move [%s] to [%s] failed: %w", move.from, move.to, renameErr)}, rollbackErrors...)...)
}
completed = append(completed, move)View on GitHub (pinned to 8641553a1f)
Solutions
- Inspect the wrapped %w error to distinguish permission vs I/O vs transient causes
- Ensure no other SiYuan instance or sync process is writing to the workspace during migration; restart and retry
- Fix destination directory permissions so Lstat can complete
- If persistent, verify device storage health / reinstall the app to recreate a clean container
Defensive patterns
Strategy: retry
Validate before calling
// Go: confirm destination directory is stat-able and stable before migrating
dest := filepath.Join(base, "siyuan")
if fi, err := os.Stat(dest); err != nil || !fi.IsDir() {
return fmt.Errorf("destination %s not a usable directory", dest)
} Try / catch
// Go
for attempt := 0; attempt < 3; attempt++ {
migrated, err := migrateLegacyIOSWorkspace(base)
if err == nil { break }
if !strings.Contains(err.Error(), "stat destination") { return err }
time.Sleep(200 * time.Millisecond) // transient concurrent-access failure
} Prevention
- Ensure only one SiYuan instance touches the workspace during migration
- Disable sync clients while migration runs
- Reboot once if I/O errors persist - they often indicate a transient kernel/filesystem issue
When it happens
Trigger: migrateLegacyIOSWorkspace's destination Lstat hits EACCES/EIO or similar on <base>/siyuan/<name> after the destination ReadDir already succeeded.
Common situations: Race with another process creating/removing files in the destination concurrently; transient I/O errors; permission changes mid-run; filesystem corruption on the device.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- stat source [%s] failed: %w
- read destination [%s] failed: %w
- destination [%s] is not empty
- destination [%s] already exists
- rollback [%s] to [%s] failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/23f4b3f2c78234c0.
Report an issue: GitHub.