{"record":{"id":"f847f2cfb2e7e80b","repo":"siyuan-note/siyuan","slug":"destination-s-already-exists","errorCode":null,"errorMessage":"destination [%s] already exists","messagePattern":"destination \\[(.+?)\\] already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/working_mobile.go","lineNumber":186,"sourceCode":"\t\treturn false, fmt.Errorf(\"read destination [%s] failed: %w\", defaultWorkspaceDir, readErr)\n\t}\n\tif 0 < len(destinationEntries) {\n\t\treturn false, fmt.Errorf(\"destination [%s] is not empty\", defaultWorkspaceDir)\n\t}\n\n\tvar moves []workspaceDirMove\n\tfor _, name := range legacyIOSWorkspaceEntries {\n\t\tfrom := filepath.Join(workspaceBaseDir, name)\n\t\tif _, statErr := os.Lstat(from); statErr != nil {\n\t\t\tif errors.Is(statErr, fs.ErrNotExist) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn false, fmt.Errorf(\"stat source [%s] failed: %w\", from, statErr)\n\t\t}\n\n\t\tto := filepath.Join(defaultWorkspaceDir, name)\n\t\tif _, statErr := os.Lstat(to); statErr == nil {\n\t\t\treturn false, fmt.Errorf(\"destination [%s] already exists\", to)\n\t\t} else if !errors.Is(statErr, fs.ErrNotExist) {\n\t\t\treturn false, fmt.Errorf(\"stat destination [%s] failed: %w\", to, statErr)\n\t\t}\n\t\tmoves = append(moves, workspaceDirMove{from: from, to: to})\n\t}\n\n\tvar completed []workspaceDirMove\n\tfor _, move := range moves {\n\t\tif renameErr := os.Rename(move.from, move.to); renameErr != nil {\n\t\t\tvar rollbackErrors []error\n\t\t\tfor i := len(completed) - 1; 0 <= i; i-- {\n\t\t\t\tcompletedMove := completed[i]\n\t\t\t\tif rollbackErr := os.Rename(completedMove.to, completedMove.from); rollbackErr != nil {\n\t\t\t\t\trollbackErrors = append(rollbackErrors, fmt.Errorf(\"rollback [%s] to [%s] failed: %w\",\n\t\t\t\t\t\tcompletedMove.to, completedMove.from, rollbackErr))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false, errors.Join(append([]error{fmt.Errorf(\"move [%s] to [%s] failed: %w\", move.from, move.to, renameErr)}, rollbackErrors...)...)","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/working_mobile.go#L168-L204","documentation":"For each legacy entry that exists in the source, migration checks that the corresponding path inside <base>/siyuan does not already exist (os.Lstat succeeds means it exists). This error prevents silently overwriting an existing file or directory in the destination workspace.","triggerScenarios":"migrateLegacyIOSWorkspace finds an entry named in legacyIOSWorkspaceEntries both in the legacy location and already present inside <base>/siyuan.","commonSituations":"A previous partial migration copied some entries but crashed before finishing; the app recreated default files (e.g. conf) that collide with legacy names; the user copied data manually.","solutions":["Decide which copy wins: remove or rename the colliding entry inside <base>/siyuan, then restart to rerun migration","If the existing destination entry is from a partial migration and incomplete, delete it so the legacy source is moved cleanly","Back up both copies before deleting; SiYuan cannot merge them automatically","Keep legacy data untouched until migration reports success - the source is only renamed, not deleted"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Go: detect name collisions ahead of migration\nfor _, name := range legacyIOSWorkspaceEntries {\n    if _, err := os.Lstat(filepath.Join(base, \"siyuan\", name)); err == nil {\n        return fmt.Errorf(\"pre-check: %s already exists in destination; resolve before migrating\", name)\n    }\n}","typeGuard":null,"tryCatchPattern":"// Go\nif err != nil && strings.Contains(err.Error(), \"already exists\") {\n    // resolve manually: remove/rename the colliding destination entry, then retry migration\n    return err\n}","preventionTips":["After a failed migration, verify no partial moves landed in the default dir before retrying","Back up both source and destination before manual cleanup","Run migration atomically on first launch to avoid concurrent creation of colliding files"],"tags":["go","filesystem","ios","migration","name-conflict"],"backgroundTag":"file-already-exists","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}