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
- Ensure the default workspace dir <workspaceBaseDir>/siyuan is created before invoking migration (create it with os.MkdirAll when it does not exist)
- Check the wrapped %w error: fs.ErrNotExist means create the dir first; permission errors mean fix container/sandbox permissions
- If the path exists but is a file, remove or rename it manually and reinstall/restart
- 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
- Always create the default workspace directory before attempting migration
- Trigger migration before first workspace initialization so the destination is empty
- Check the wrapped fs.PathError to distinguish not-exist vs permission causes
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
- destination [%s] is not empty
- stat source [%s] failed: %w
- destination [%s] already exists
- stat destination [%s] failed: %w
- rollback [%s] to [%s] failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/0714deec81fb2fff.
Report an issue: GitHub.