siyuan-note/siyuan · error
destination [%s] is not empty
Error message
destination [%s] is not empty
What it means
During legacy iOS workspace migration, the destination <workspaceBaseDir>/siyuan must be empty before entries are moved into it. This error is returned when ReadDir succeeds but the directory already contains entries, to avoid overwriting or mixing with an existing workspace.
Source
Thrown at kernel/util/working_mobile.go:171
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)
} else if !errors.Is(statErr, fs.ErrNotExist) {
return false, fmt.Errorf("stat destination [%s] failed: %w", to, statErr)
}View on GitHub (pinned to 8641553a1f)
Solutions
- If the default workspace already has data you want, keep it and manually move needed legacy files yourself instead of migrating
- If the default workspace content is disposable, empty <base>/siyuan and restart the app to rerun migration
- Check whether a previous migration half-completed (files present but legacy dirs remain); complete the move manually
- Ensure migration runs before first workspace initialization on fresh installs so the destination is still empty
Defensive patterns
Strategy: validation
Validate before calling
// Go: verify the destination is empty before starting migration
dest := filepath.Join(base, "siyuan")
entries, err := os.ReadDir(dest)
if err == nil && len(entries) > 0 {
return fmt.Errorf("aborting migration: destination %s already has %d entries", dest, len(entries))
} Try / catch
// Go
migrated, err := migrateLegacyIOSWorkspace(base)
if err != nil && strings.Contains(err.Error(), "is not empty") {
// default workspace already initialized; skip migration and keep current data
return nil // or prompt the user to resolve manually
} Prevention
- Run migration as the very first step on a fresh install, before any workspace content is created
- Clean up leftovers from previously failed migration attempts before retrying
- Never manually pre-populate the default siyuan/ directory on devices with legacy data
When it happens
Trigger: migrateLegacyIOSWorkspace (via initWorkspaceDirMobile) finds len(destinationEntries) > 0 in <base>/siyuan on a device where the default workspace was already initialized before legacy migration ran.
Common situations: User already opened the app once on the new version (creating conf/ etc. in siyuan/) before legacy data migration triggered; partial previous migration left files behind; user manually created content in the default workspace.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- read destination [%s] failed: %w
- 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/559487e93d3ea6c2.
Report an issue: GitHub.