siyuan-note/siyuan · critical

rollback [%s] to [%s] failed: %w

Error message

rollback [%s] to [%s] failed: %w

What it means

During the move loop of legacy iOS workspace migration, an os.Rename failed and rollback of previously completed moves also failed for one or more entries. The returned error joins the original move failure with all rollback failures, so the workspace may be left in a partially migrated, unrolled-back state.

Source

Thrown at kernel/util/working_mobile.go:200

		}

		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)
	}

	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

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Manually inspect both directories and finish or undo the moves by hand, matching the logged move list (from/to pairs)
  2. If rollback failures mention EXDEV (cross-device link), migrate via copy+delete instead of rename, or ensure both dirs share one volume
  3. Free device storage and retry; rename and rollback both need space for directory updates
  4. Back up the legacy entries before any manual intervention; on success, the migrated default workspace will be usable
  5. Restart the app afterwards - migration skips entries whose sources no longer exist, so a hand-completed state is stable

Example fix

// before (rename fails across volumes)
os.Rename(move.from, move.to)
// after: fall back to copy+remove for cross-filesystem moves
if err := copyAll(move.from, move.to); err == nil { os.RemoveAll(move.from) }
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify same-filesystem rename is possible before migrating (avoid EXDEV)
sameVol, _ := sameFilesystem(base, filepath.Join(base, "siyuan"))
if !sameVol { return errors.New("cannot migrate across filesystems safely") }

Try / catch

// Go
if err != nil {
    var joined interface{ Unwrap() []error }
    if errors.As(err, &joined) {
        for _, e := range errors.Unwrap(err).(interface{ Errors() []error }).Errors() {
            log.Printf("migration rollback component: %v", e) // inspect each move/rollback failure
        }
    }
    // manually reconcile directories per logged from/to pairs before retrying
}

Prevention

When it happens

Trigger: In migrateLegacyIOSWorkspace, os.Rename(completedMove.to, completedMove.from) errors while unwinding after a failed move - typically because the 'to' or 'from' path is no longer accessible mid-operation.

Common situations: Cross-filesystem rename on containers where legacy dir and default dir are on different volumes (rename fails with EXDEV); storage filled up mid-migration; sandbox revoking access between operations; process kill during migration leaving mixed state.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/6c39c915c0ed9c21. Report an issue: GitHub.