siyuan-note/siyuan · critical
move [%s] to [%s] failed: %w
Error message
move [%s] to [%s] failed: %w
What it means
The initial os.Rename of a legacy workspace entry from its old location to <base>/siyuan failed. On failure, migration attempts to rename back all already-completed moves and returns the joined error, so no data loss occurs but the workspace is not migrated.
Source
Thrown at kernel/util/working_mobile.go:204
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)
}
for _, move := range completed {
logging.LogInfof("moved legacy iOS workspace dir [from=%s, to=%s]", move.from, move.to)
}
return true, nil
}
func replaceLegacyIOSWorkspacePath(workspacePaths []string, workspaceBaseDir, defaultWorkspaceDir string) []string {
for i, workspacePath := range workspacePaths {
if filepath.Clean(workspacePath) == filepath.Clean(workspaceBaseDir) {
workspacePaths[i] = defaultWorkspaceDir
}
}
return DeduplicateWorkspacePaths(workspacePaths)
}View on GitHub (pinned to 8641553a1f)
Solutions
- Read the wrapped %w error: EXDEV means use copy+delete; ENOENT means re-check which side is missing
- Ensure both source and destination live on the same volume/container path
- Close other SiYuan instances and retry the migration
- Free storage space if ENOSPC, then restart the app to rerun migration (it is idempotent per-entry: missing sources are skipped)
- If rollback errors were joined, manually reconcile the directories as described in the log
Defensive patterns
Strategy: retry
Validate before calling
// Go: pre-flight both endpoints of every move
for _, name := range legacyIOSWorkspaceEntries {
if _, err := os.Lstat(filepath.Join(base, name)); err != nil { continue }
if _, err := os.Lstat(filepath.Join(base, "siyuan", name)); err == nil {
return fmt.Errorf("pre-flight: %s would collide", name)
}
} Try / catch
// Go
migrated, err := migrateLegacyIOSWorkspace(base)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EXDEV) {
// fall back to copy+delete migration across filesystems
err = migrateByCopy(base)
}
} Prevention
- Ensure source and destination are on the same filesystem/volume
- Free storage space before first-launch migration
- Retry is safe: migration skips entries whose sources no longer exist
- Check the joined error list for accompanying rollback failures before retrying
When it happens
Trigger: migrateLegacyIOSWorkspace calls os.Rename(move.from, move.to) and it errors - source vanished, destination created concurrently, EXDEV across filesystems, or permission failure.
Common situations: Legacy dir and default dir on different volumes (cross-device rename); another process locking/holding files; disk full; the app killed mid-migration previously leaving inconsistent expectations.
Related errors
- rollback [%s] to [%s] failed: %w
- read destination [%s] failed: %w
- destination [%s] is not empty
- stat source [%s] failed: %w
- destination [%s] already exists
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/c8518aa3702cf95a.
Report an issue: GitHub.