siyuan-note/siyuan · error

read destination [%s] failed: %w

Error message

read destination [%s] failed: %w

What it means

migrateLegacyIOSWorkspace moves legacy iOS workspace entries into the default workspace dir <base>/siyuan. Before moving, it lists the destination with os.ReadDir; this error wraps any such failure, including the destination not existing or being unreadable. The migration aborts without touching source data.

Source

Thrown at kernel/util/working_mobile.go:168

	from string
	to   string
}

func migrateLegacyIOSWorkspace(workspaceBaseDir string) (migrated bool, err error) {
	if ContainerIOS != Container || !gulu.File.IsDir(workspaceBaseDir) {
		return false, nil
	}

	for _, name := range []string{"conf", "data", "temp"} {
		if !gulu.File.IsDir(filepath.Join(workspaceBaseDir, name)) {
			return false, nil
		}
	}

	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)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the default workspace dir <workspaceBaseDir>/siyuan is created before invoking migration (create it with os.MkdirAll when it does not exist)
  2. Check the wrapped %w error: fs.ErrNotExist means create the dir first; permission errors mean fix container/sandbox permissions
  3. If the path exists but is a file, remove or rename it manually and reinstall/restart
  4. Re-run the app; migration is skipped safely once the destination is readable and empty or already migrated

Example fix

// before
if _, err := os.Stat(defaultWorkspaceDir); os.IsNotExist(err) {
    if err := os.MkdirAll(defaultWorkspaceDir, 0755); err != nil { return false, err }
}
// after: ReadDir succeeds and migration proceeds
// (guard migrateLegacyIOSWorkspace so a not-exist destination creates the dir instead of erroring)
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: pre-check the destination before migration
base := workspaceBaseDir
dest := filepath.Join(base, "siyuan")
if _, err := os.ReadDir(dest); err != nil {
    if os.IsNotExist(err) { os.MkdirAll(dest, 0755) } // create so ReadDir succeeds
}

Try / catch

// Go
migrated, err := migrateLegacyIOSWorkspace(base)
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrNotExist) {
        os.MkdirAll(filepath.Join(base, "siyuan"), 0755)
        migrated, err = migrateLegacyIOSWorkspace(base) // retry once
    }
}

Prevention

When it happens

Trigger: initWorkspaceDirMobile triggers migration when <workspaceBaseDir>/siyuan cannot be ReadDir: missing directory, permission denied, or path is a file.

Common situations: Fresh iOS/HarmonyOS install where <base>/siyuan has not been created yet; sandbox permission changes after an OS upgrade; a stray file named 'siyuan' in the container's documents dir.

Related errors


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