{"record":{"id":"ec33475702fbe466","repo":"siyuan-note/siyuan","slug":"move-notebook-s-file-s-failed-s","errorCode":null,"errorMessage":"Move notebook [%s] file [%s] failed: %s","messagePattern":"Move notebook \\[(.+?)\\] file \\[(.+?)\\] failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/box.go","lineNumber":466,"sourceCode":"\tIncSync()\n\treturn nil\n}\n\nfunc (box *Box) Move(oldPath, newPath string) error {\n\tif _, err := box.validateBoxPath(oldPath); err != nil {\n\t\treturn err\n\t}\n\tif _, err := box.validateBoxPath(newPath); err != nil {\n\t\treturn err\n\t}\n\tboxLocalPath := filepath.Join(util.DataDir, box.ID)\n\tfromPath := filepath.Join(boxLocalPath, oldPath)\n\ttoPath := filepath.Join(boxLocalPath, newPath)\n\n\tif err := filelock.Rename(fromPath, toPath); err != nil {\n\t\tmsg := fmt.Sprintf(Conf.Language(5), box.Name, fromPath, err)\n\t\tlogging.LogErrorf(\"move [path=%s] in box [%s] failed: %s\", fromPath, box.Name, err)\n\t\treturn errors.New(msg)\n\t}\n\n\tif oldDir := path.Dir(oldPath); ast.IsNodeIDPattern(path.Base(oldDir)) {\n\t\tfromDir := filepath.Join(boxLocalPath, oldDir)\n\t\tif util.IsEmptyDir(fromDir) {\n\t\t\tfilelock.Remove(fromDir)\n\t\t}\n\t}\n\tIncSync()\n\treturn nil\n}\n\nfunc (box *Box) Remove(path string) error {\n\tif _, err := box.validateBoxPath(path); err != nil {\n\t\treturn err\n\t}\n\tboxLocalPath := filepath.Join(util.DataDir, box.ID)\n\tfilePath := filepath.Join(boxLocalPath, path)","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/box.go#L448-L484","documentation":"Box.Move renames a file/folder within a notebook via filelock.Rename and wraps failure in localized message 5 'Move notebook [%s] file [%s] failed: %s'. After a successful move it also cleans up an emptied ID-named parent dir. Because rename(2) semantics apply, an existing destination, a locked source, or a cross-device path will fail.","triggerScenarios":"POST /api/filetree/renameDoc / move paths where newPath already exists, the source .sy file is held by another process, or old and new paths resolve to different filesystems.","commonSituations":"Moving docs onto a destination name that collides with an existing doc; sync clients locking .sy files; workspace data split across mounts (e.g. symlinked notebook dirs).","solutions":["Check that newPath does not exist before moving (Box.Exist) and pick a unique name","Close/stop other processes holding the source file (second kernel instance, sync agent, editor with the file open)","Keep the notebook data on one filesystem; replace symlinked notebook dirs with real ones"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (box.Exist(newPath)) throw new Error('destination already exists: ' + newPath);\nif (!box.Exist(oldPath)) throw new Error('source missing: ' + oldPath);\nbox.Move(oldPath, newPath);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick unique destination names before renaming","Keep notebook data on a single filesystem (no symlinked cross-device dirs)","Stop sync agents before bulk moves"],"tags":["filesystem","notebook","move","api"],"backgroundTag":"file-rename-failed","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}